import * as LocalAuthentication from "expo-local-authentication";
import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
  type ReactNode,
} from "react";
import { AppState, type AppStateStatus } from "react-native";

import {
  clearStoredPin,
  getAppLockEnabled,
  getBiometricEnabled,
  getStoredPin,
  isValidPin,
  setAppLockEnabled,
  setBiometricEnabled,
  setStoredPin,
} from "@/lib/app-lock";

type AppLockContextValue = {
  enabled: boolean;
  biometricEnabled: boolean;
  hasPin: boolean;
  isLocked: boolean;
  biometricAvailable: boolean;
  biometricLabel: string;
  unlockWithPin: (pin: string) => Promise<boolean>;
  unlockWithBiometric: () => Promise<boolean>;
  enableLock: (pin: string) => Promise<void>;
  disableLock: (pin: string) => Promise<boolean>;
  changePin: (currentPin: string, nextPin: string) => Promise<boolean>;
  setUseBiometric: (enabled: boolean) => Promise<void>;
  lock: () => void;
};

const AppLockContext = createContext<AppLockContextValue | null>(null);

export function AppLockProvider({ children }: { children: ReactNode }) {
  const [enabled, setEnabled] = useState(false);
  const [biometricEnabled, setBiometricEnabledState] = useState(false);
  const [hasPin, setHasPin] = useState(false);
  const [isLocked, setIsLocked] = useState(false);
  const [biometricAvailable, setBiometricAvailable] = useState(false);
  const [biometricLabel, setBiometricLabel] = useState("Biometrics");
  const wasBackgrounded = useRef(false);
  const hydrated = useRef(false);

  useEffect(() => {
    let cancelled = false;

    async function hydrate() {
      const [lockEnabled, pin, bioEnabled, hasHardware, isEnrolled, authTypes] =
        await Promise.all([
          getAppLockEnabled(),
          getStoredPin(),
          getBiometricEnabled(),
          LocalAuthentication.hasHardwareAsync(),
          LocalAuthentication.isEnrolledAsync(),
          LocalAuthentication.supportedAuthenticationTypesAsync(),
        ]);

      if (cancelled) return;

      const bioAvailable = hasHardware && isEnrolled;
      setEnabled(lockEnabled);
      setHasPin(Boolean(pin));
      setBiometricEnabledState(bioEnabled && bioAvailable);
      setBiometricAvailable(bioAvailable);
      setBiometricLabel(
        authTypes.includes(LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION)
          ? "Face ID"
          : authTypes.includes(LocalAuthentication.AuthenticationType.FINGERPRINT)
            ? "Touch ID"
            : "Biometrics",
      );
      setIsLocked(lockEnabled);
      hydrated.current = true;
    }

    void hydrate();
    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    const subscription = AppState.addEventListener("change", (nextState: AppStateStatus) => {
      if (!hydrated.current || !enabled) return;

      if (nextState === "background" || nextState === "inactive") {
        wasBackgrounded.current = true;
      }

      if (nextState === "active" && wasBackgrounded.current) {
        wasBackgrounded.current = false;
        setIsLocked(true);
      }
    });

    return () => subscription.remove();
  }, [enabled]);

  const unlockWithPin = useCallback(async (pin: string) => {
    const stored = await getStoredPin();
    if (!stored || stored !== pin) {
      return false;
    }
    setIsLocked(false);
    return true;
  }, []);

  const unlockWithBiometric = useCallback(async () => {
    if (!biometricEnabled || !biometricAvailable) {
      return false;
    }

    const result = await LocalAuthentication.authenticateAsync({
      promptMessage: "Unlock beenvoice",
      cancelLabel: "Use PIN",
      disableDeviceFallback: true,
    });

    if (!result.success) {
      return false;
    }

    setIsLocked(false);
    return true;
  }, [biometricAvailable, biometricEnabled]);

  const enableLock = useCallback(async (pin: string) => {
    if (!isValidPin(pin)) {
      throw new Error("PIN must be 4–6 digits");
    }
    await setStoredPin(pin);
    await setAppLockEnabled(true);
    setHasPin(true);
    setEnabled(true);
    setIsLocked(false);
  }, []);

  const disableLock = useCallback(async (pin: string) => {
    const stored = await getStoredPin();
    if (!stored || stored !== pin) {
      return false;
    }
    await setAppLockEnabled(false);
    await clearStoredPin();
    await setBiometricEnabled(false);
    setEnabled(false);
    setHasPin(false);
    setBiometricEnabledState(false);
    setIsLocked(false);
    return true;
  }, []);

  const changePin = useCallback(async (currentPin: string, nextPin: string) => {
    const stored = await getStoredPin();
    if (!stored || stored !== currentPin || !isValidPin(nextPin)) {
      return false;
    }
    await setStoredPin(nextPin);
    return true;
  }, []);

  const setUseBiometric = useCallback(async (next: boolean) => {
    if (next) {
      const result = await LocalAuthentication.authenticateAsync({
        promptMessage: `Enable ${biometricLabel}`,
        cancelLabel: "Cancel",
        disableDeviceFallback: true,
      });
      if (!result.success) return;
    }
    await setBiometricEnabled(next);
    setBiometricEnabledState(next);
  }, [biometricLabel]);

  const lock = useCallback(() => {
    if (enabled) {
      setIsLocked(true);
    }
  }, [enabled]);

  const value = useMemo(
    () => ({
      enabled,
      biometricEnabled,
      hasPin,
      isLocked,
      biometricAvailable,
      biometricLabel,
      unlockWithPin,
      unlockWithBiometric,
      enableLock,
      disableLock,
      changePin,
      setUseBiometric,
      lock,
    }),
    [
      enabled,
      biometricEnabled,
      hasPin,
      isLocked,
      biometricAvailable,
      biometricLabel,
      unlockWithPin,
      unlockWithBiometric,
      enableLock,
      disableLock,
      changePin,
      setUseBiometric,
      lock,
    ],
  );

  return <AppLockContext.Provider value={value}>{children}</AppLockContext.Provider>;
}

export function useAppLock() {
  const context = useContext(AppLockContext);
  if (!context) {
    throw new Error("useAppLock must be used within AppLockProvider");
  }
  return context;
}
