import React, { useEffect, useRef, useState } from "react";
import { Platform, Pressable, View } from "react-native";
import { Ionicons } from "@expo/vector-icons";
import Toast from "react-native-toast-message";
import Animated, { Easing, useAnimatedStyle, useSharedValue, withRepeat, withTiming, } from "react-native-reanimated";
import { useColorScheme } from "nativewind";
import { Button } from "../../components/button";
import { PasswordInput } from "../../components/input";
import { MutedText, Text } from "../../components/text";
import { useAppConfig } from "../../context/AppConfigContext";
/**
 * Unlock form for session-locked flows (e.g. vault re-lock after idle).
 * The typed password never leaves this component — it is captured into a
 * local closure variable and wiped from React state before the async
 * unlock work begins so the fiber tree no longer references the
 * plaintext during PBKDF2 / WebCrypto derivation.
 */
export function LockOverlay({ onUnlock, successMessage, onBiometricUnlock, autoPromptBiometric = true, }) {
    const { appName, Logo } = useAppConfig();
    const { colorScheme } = useColorScheme();
    const isDark = colorScheme === "dark";
    const [password, setPassword] = useState("");
    const [loading, setLoading] = useState(false);
    const [biometricState, setBiometricState] = useState("idle");
    const [error, setError] = useState("");
    const pulse = useSharedValue(1);
    const hasAutoPrompted = useRef(false);
    // Two distinct screens, not one screen with an extra button:
    //   "biometric" — fingerprint icon only. No password field, no Unlock
    //                 button anywhere in the tree.
    //   "password"  — the normal password form, shown once biometric
    //                 unlock isn't offered at all, OR once an attempt has
    //                 settled (succeeded or failed — a real success
    //                 unmounts this whole overlay from the parent almost
    //                 immediately, so briefly being in "password" mode in
    //                 that case is never visible).
    // The password field being ABSENT from the tree during "biometric"
    // mode is load-bearing, not cosmetic: Android's Autofill Framework
    // triggers independently of the passkey ceremony, purely from a
    // password-type field being mounted and focusable on screen. With
    // both mounted together, the passkey ceremony's own Credential
    // Manager UI and Bitwarden's separately-triggered autofill prompt
    // fought each other — dismiss one, the other reopens immediately,
    // neither settling — reported as a black screen the app never
    // recovered from, root-caused on 2026-08-17. There is no such field
    // for Android to react to until biometric unlock has actually failed.
    const [mode, setMode] = useState(onBiometricUnlock && autoPromptBiometric ? "biometric" : "password");
    const triggerBiometricUnlock = React.useCallback(async () => {
        if (!onBiometricUnlock) {
            return;
        }
        setBiometricState("prompting");
        try {
            await onBiometricUnlock();
        }
        finally {
            setBiometricState("idle");
            setMode("password");
        }
    }, [onBiometricUnlock]);
    useEffect(() => {
        if (onBiometricUnlock && autoPromptBiometric && !hasAutoPrompted.current) {
            hasAutoPrompted.current = true;
            void triggerBiometricUnlock();
        }
    }, [onBiometricUnlock, autoPromptBiometric, triggerBiometricUnlock]);
    useEffect(() => {
        if (biometricState === "prompting") {
            pulse.value = withRepeat(withTiming(1.12, { duration: 650, easing: Easing.inOut(Easing.ease) }), -1, true);
        }
        else {
            pulse.value = withTiming(1, { duration: 200 });
        }
    }, [biometricState, pulse]);
    const pulseStyle = useAnimatedStyle(() => ({
        transform: [{ scale: pulse.value }],
    }));
    const handleUnlock = async () => {
        const captured = password;
        if (!captured.trim()) {
            setError("Password is required");
            return;
        }
        setPassword("");
        setLoading(true);
        setError("");
        try {
            await onUnlock(captured);
            Toast.show({
                type: "success",
                text1: successMessage ?? `${appName} unlocked`,
                visibilityTime: 1500,
            });
        }
        catch (e) {
            const msg = e instanceof Error ? e.message : String(e);
            if (__DEV__) {
                console.error("Unlock failed:", msg);
            }
            // Distinguish "wrong password" (the common case) from a real
            // crypto/transport failure. The vault key-derivation pipeline
            // surfaces wrong-password as a decrypt / authentication-tag
            // mismatch from AES-GCM; anything else means the unlock path
            // itself broke and the user can't recover by retyping.
            const looksLikeWrongPassword = /decrypt|auth(entication)? tag|bad ?decrypt|invalid|unwrap/i.test(msg);
            setError(looksLikeWrongPassword ? "Incorrect password. Please try again." : `Unlock failed: ${msg}`);
        }
        finally {
            setLoading(false);
        }
    };
    if (mode === "biometric") {
        return (<View className="flex-1 items-center justify-center bg-background dark:bg-black px-6">
        <View className="mb-8 flex-row items-center justify-center">
          <Logo width={56} height={56}/>
          <Text className="font-sans-bold text-[28px] text-foreground dark:text-white">
            {appName}
          </Text>
        </View>
        <Pressable onPress={triggerBiometricUnlock} accessibilityRole="button" accessibilityLabel="Unlock with biometrics" testID="lock-biometric-icon" className="items-center">
          <Animated.View style={pulseStyle} className="h-24 w-24 items-center justify-center rounded-full bg-neutral-100 dark:bg-neutral-900">
            <Ionicons name="finger-print" size={52} color={isDark ? "#FFFFFF" : "#000000"}/>
          </Animated.View>
          <MutedText className="mt-3 text-sm">
            {biometricState === "prompting" ? "Authenticating…" : "Tap to unlock"}
          </MutedText>
        </Pressable>
      </View>);
    }
    return (<View className="flex-1 items-center justify-center bg-background dark:bg-black">
      <View className="w-full max-w-[340px] items-center px-6">
        <View className="mb-2 flex-row items-center justify-center">
          <Logo width={56} height={56}/>
          <Text className="font-sans-bold text-[28px] text-foreground dark:text-white">
            {appName}
          </Text>
        </View>
        <MutedText className="mb-8 text-center text-sm">
          Enter your master password to unlock
        </MutedText>

        <View className="w-full gap-3">
          <PasswordInput value={password} onChangeText={(text) => {
            setPassword(text);
            if (error) {
                setError("");
            }
        }} placeholder="Master password" error={error} 
    // Web: autofocus on a login-style page is expected desktop
    // UX. Native: autofocus here pops the on-screen keyboard
    // unprompted the moment the password fallback appears —
    // reported as a real annoyance previously. Wait for an
    // explicit tap on the field.
    autoFocus={Platform.OS === "web"} returnKeyType="done" onSubmitEditing={handleUnlock} accessibilityLabel="Master password" testID="lock-password-input"/>
          <Button className="w-full" onPress={handleUnlock} isLoading={loading} accessibilityLabel="Unlock" accessibilityRole="button" testID="lock-unlock-button">
            Unlock
          </Button>
          {onBiometricUnlock ? (<Button variant="outline" className="w-full" onPress={() => setMode("biometric")} accessibilityLabel="Unlock with biometrics instead" accessibilityRole="button" testID="lock-retry-biometric-button">
              Unlock with Biometrics Instead
            </Button>) : null}
        </View>
      </View>
    </View>);
}
//# sourceMappingURL=LockOverlay.jsx.map