import React, { useEffect, useRef, useState } from "react";
import { ScrollView, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { useNavigation } from "../../context/NavigationContext";
import { StatusBar } from "expo-status-bar";
import { Ionicons } from "@expo/vector-icons";
import { useColorScheme } from "nativewind";
import * as Haptics from "expo-haptics";
import { Heading, MutedText, Text } from "../../components/text";
import { Button, IconButton } from "../../components/button";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "../../components/card";
import { withClickTracking } from "../../services/telemetry";
import { EmailInput, PasswordInput } from "../../components/input";
import { Dialog, DialogButton, DialogFooter } from "../../components/dialog";
import { SeparatorWithText } from "../../components/separator";
import { useAuth } from "../../hooks/useAuth";
import { ApiError } from "../../utils/http-error";
import { validateEmail } from "../../utils/validation";
import { useShakeAnimation } from "../../hooks/useShakeAnimation";
import ShakeField from "../../forms/shake-field";
import FormError from "../../forms/form-error";
import { useAppConfig } from "../../context/AppConfigContext";
const SCROLL_CONTENT_STYLE = { flexGrow: 1 };
export function LoginScreen({ topAccessory, socialAuth, passwordPlaceholder = "Enter your password", passwordLabel = "Password", passkeyEnabled = false, passkeyUnlockPlaceholder = "Enter your master password", passkeyUnlockTitle = "Unlock your vault", passkeyUnlockDescription = "Signed in successfully. This passkey can't unlock the vault on its own — enter your master password to decrypt your data.", } = {}) {
    const { login: authLogin, loginWithPasskey, unlockWithPassword } = useAuth();
    const router = useNavigation();
    const { appName, Logo, routes } = useAppConfig();
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");
    const [isLoading, setIsLoading] = useState(false);
    const [errors, setErrors] = useState({});
    // Passkey ceremony state machine.
    //   "idle"            — default; primary CTA is the email/password form.
    //   "passkey-loading" — passkey ceremony is in flight.
    //   "passkey-unlock"  — server accepted the assertion but the
    //                       authenticator did not honor PRF; collect the
    //                       master password to complete local unlock.
    const [passkeyStep, setPasskeyStep] = useState("idle");
    const [unlockPassword, setUnlockPassword] = useState("");
    const [unlockError, setUnlockError] = useState("");
    const [pendingOnboardingStep, setPendingOnboardingStep] = useState(null);
    // Abort an in-flight passkey ceremony on unmount so the OS prompt
    // tears down and the resolved credential doesn't land on a stale
    // setState. Ref + cleanup so navigating away mid-ceremony is clean.
    const passkeyAbortRef = useRef(null);
    useEffect(() => () => {
        passkeyAbortRef.current?.abort();
    }, []);
    const { colorScheme } = useColorScheme();
    const isDark = colorScheme === "dark";
    const emailShake = useShakeAnimation();
    const passwordShake = useShakeAnimation();
    const firstError = errors.email ?? errors.password;
    const validateForm = () => {
        const newErrors = {};
        if (!email) {
            newErrors.email = "Email is required";
        }
        else if (!validateEmail(email)) {
            newErrors.email = "Please enter a valid email";
        }
        if (!password) {
            newErrors.password = "Password is required";
        }
        else if (password.length < 8) {
            newErrors.password = "Password must be at least 8 characters";
        }
        return newErrors;
    };
    const performLogin = async () => {
        setIsLoading(true);
        try {
            await authLogin(email.trim().toLowerCase(), password);
            setPassword("");
            await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
        }
        catch (error) {
            await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
            const errorMessage = error instanceof Error ? error.message : "Login failed";
            // Prefer the server's machine-readable `error.code` over
            // substring-matching the English message — i18n on the server
            // (or a wording tweak) silently broke the old branches.
            const code = error instanceof ApiError ? error.code : undefined;
            // Codes the server emits today. Unknown / missing codes fall
            // through to the generic "show the server's message" branch.
            if (code === "INVALID_CREDENTIALS") {
                setErrors({ password: "Invalid email or password" });
                emailShake.shake();
                passwordShake.shake();
            }
            else if (code === "ACCOUNT_LOCKED") {
                setErrors({ email: "Account is locked. Please contact support." });
                emailShake.shake();
            }
            else {
                setErrors({ password: errorMessage });
                passwordShake.shake();
            }
        }
        finally {
            setIsLoading(false);
        }
    };
    const handleLogin = async () => {
        const newErrors = validateForm();
        if (Object.keys(newErrors).length > 0) {
            setErrors(newErrors);
            if (newErrors.email) {
                emailShake.shake();
            }
            if (newErrors.password) {
                passwordShake.shake();
            }
            await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
            return;
        }
        setErrors({});
        await performLogin();
    };
    const routeAfterUnlock = (onboardingStep) => {
        if (onboardingStep === "email_verification") {
            router.replace({ pathname: routes.verifyEmail, params: { email } });
        }
        else if (onboardingStep === "mfa_enrollment") {
            router.replace(routes.mfaEnroll);
        }
        else if (onboardingStep === "import") {
            router.replace(routes.importData);
        }
        else {
            // `complete`, null, undefined, or any unknown step the client
            // doesn't recognise — the consumer's route guard re-evaluates
            // and bounces to welcome / the right step if the server says
            // onboarding isn't done. Default to home rather than stranding
            // the user.
            router.replace(routes.home);
        }
    };
    const handlePasskeyLogin = async () => {
        setPasskeyStep("passkey-loading");
        setErrors({});
        // Abort any prior ceremony before starting a new one so a
        // double-tap doesn't race two outstanding credentials.get() calls.
        passkeyAbortRef.current?.abort();
        const controller = new AbortController();
        passkeyAbortRef.current = controller;
        try {
            const trimmedEmail = email.trim().toLowerCase();
            const result = await loginWithPasskey(trimmedEmail.length > 0 ? trimmedEmail : undefined, controller.signal);
            await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
            if (result.unlocked) {
                setPasskeyStep("idle");
                routeAfterUnlock(result.user.onboarding_step);
                return;
            }
            setPendingOnboardingStep(result.user.onboarding_step ?? null);
            setPasskeyStep("passkey-unlock");
        }
        catch (error) {
            // Distinguish user-cancel / programmatic-abort from real
            // failures. `NotAllowedError` (user cancel, timeout) and
            // `AbortError` (signal abort) shouldn't paint a destructive
            // error on the password field — the user knows they cancelled.
            // Real failures (`SecurityError`, `UnknownError`, network) do
            // want the inline message + shake so the user falls back to
            // password.
            const name = error instanceof Error ? error.name : undefined;
            const isBenign = name === "NotAllowedError" || name === "AbortError";
            if (!isBenign) {
                await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
                const message = error instanceof Error ? error.message : "Passkey sign-in failed";
                setErrors({ password: message });
            }
            setPasskeyStep("idle");
        }
    };
    const handleUnlockAfterPasskey = async () => {
        if (!unlockPassword) {
            setUnlockError("Master password is required");
            await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
            return;
        }
        setIsLoading(true);
        try {
            await unlockWithPassword(unlockPassword);
            setUnlockPassword("");
            setUnlockError("");
            await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
            routeAfterUnlock(pendingOnboardingStep ?? "complete");
            setPasskeyStep("idle");
        }
        catch (error) {
            const message = error instanceof Error ? error.message : "Failed to unlock the vault. Check your password.";
            setUnlockError(message);
            await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
        }
        finally {
            setIsLoading(false);
        }
    };
    return (<>
      <SafeAreaView className="flex-1 bg-background dark:bg-black">
        <StatusBar style={isDark ? "light" : "dark"}/>
        <ScrollView className="flex-1" contentContainerStyle={SCROLL_CONTENT_STYLE} keyboardShouldPersistTaps="handled" showsVerticalScrollIndicator={false}>
          <View className="flex-1 justify-between px-6">
            <View className="mt-4">
              <IconButton variant="ghost" telemetryId="auth.login.push" onPress={() => router.push(routes.welcome)} className="self-start" accessibilityLabel="Go back" accessibilityRole="button">
                <Ionicons name="arrow-back" size={24} color={isDark ? "#FFFFFF" : "#000000"}/>
              </IconButton>
            </View>
            {topAccessory}

            <View className="flex-1 items-center justify-center">
              <View className="mb-6 items-center">
                <View className="flex-row items-center">
                  <Logo width={72} height={72} style={{ marginRight: -2 }}/>
                  <Heading className="text-4xl">{appName}</Heading>
                </View>
              </View>

              <Card className="w-full max-w-[360px]">
                <CardHeader>
                  <CardTitle>Sign In</CardTitle>
                  <CardDescription>Enter your credentials to sign in to {appName}</CardDescription>
                </CardHeader>

                <CardContent className="gap-4">
                  {/* Passkey CTA above email/password — passkeys are
            the preferred primary factor when the device has
            one enrolled. Email is optional; when blank the
            server emits a discoverable-credential challenge
            and the platform UI picks the account. */}
                  {passkeyEnabled ? (<>
                      <Button variant="outline" className="w-full" telemetryId="auth.login.passkey_login" onPress={handlePasskeyLogin} isLoading={passkeyStep === "passkey-loading"} accessibilityLabel="Sign in with passkey" accessibilityRole="button" accessibilityHint="Use a passkey from this device to sign in" testID="login-passkey-button">
                        <View className="flex-row items-center justify-center">
                          <Ionicons name="key-outline" size={20} color={isDark ? "#FFFFFF" : "#000000"} className="mr-2"/>
                          <Text className="text-base font-medium">Sign in with passkey</Text>
                        </View>
                      </Button>
                      <SeparatorWithText />
                    </>) : null}

                  <ShakeField shake={emailShake} label="Email">
                    <EmailInput value={email} onChangeText={(text) => {
            setEmail(text);
            if (errors.email) {
                setErrors({ ...errors, email: undefined });
            }
        }} autoFocus invalid={!!errors.email} autoComplete="email" accessibilityLabel="Email address" testID="login-email-input"/>
                  </ShakeField>

                  <ShakeField shake={passwordShake} label={passwordLabel}>
                    <PasswordInput value={password} onChangeText={(text) => {
            setPassword(text);
            if (errors.password) {
                setErrors({ ...errors, password: undefined });
            }
        }} placeholder={passwordPlaceholder} invalid={!!errors.password} autoComplete="password" returnKeyType="done" onSubmitEditing={handleLogin} accessibilityLabel={passwordLabel} testID="login-password-input"/>
                  </ShakeField>

                  <View className="items-end">
                    <Text className="font-mono text-sm underline" onPress={withClickTracking("auth.login.push_2", () => router.push(routes.forgotPassword))} accessibilityRole="link" accessibilityLabel="Forgot password">
                      Forgot password?
                    </Text>
                  </View>
                </CardContent>

                <CardFooter className="flex-col items-stretch gap-3 border-t-0">
                  <MutedText className="text-center text-sm">
                    Don&apos;t have an account?{" "}
                    <Text className="text-sm text-foreground underline" onPress={withClickTracking("auth.login.push_3", () => router.push(routes.register))} accessibilityRole="link" accessibilityLabel="Sign up">
                      Sign Up
                    </Text>
                  </MutedText>

                  <FormError message={firstError}/>

                  <Button className="w-full" telemetryId="auth.login.login" onPress={handleLogin} isLoading={isLoading} accessibilityLabel="Sign in" accessibilityRole="button" accessibilityHint="Double tap to sign in with your credentials" testID="login-submit">
                    Sign In
                  </Button>

                  {socialAuth ? (<>
                      <SeparatorWithText />
                      {socialAuth}
                    </>) : null}
                </CardFooter>
              </Card>
            </View>

            <View className="mb-6"/>
          </View>
        </ScrollView>
      </SafeAreaView>

      {/* Passkey unlock dialog — shown only when the authenticator
            did NOT honor the PRF extension. The session is already
            established server-side; this prompt gathers the master
            password so the consumer's `unlockWithPassword` handler can
            derive the symmetric key locally before routing. */}
      <Dialog visible={passkeyStep === "passkey-unlock"} onClose={() => {
            setPasskeyStep("idle");
            setUnlockPassword("");
            setUnlockError("");
        }} draggable dismissOnBackdrop={false}>
        <View className="mb-5 items-center">
          <View className={`mb-4 h-14 w-14 items-center justify-center rounded-full ${isDark ? "bg-white/10" : "bg-black/5"}`}>
            <Ionicons name="lock-open-outline" size={28} color={isDark ? "#FFFFFF" : "#000000"}/>
          </View>
          <Heading className="mb-2 text-xl">{passkeyUnlockTitle}</Heading>
          <Text className="mb-3 text-center text-sm leading-5">{passkeyUnlockDescription}</Text>
          <PasswordInput value={unlockPassword} onChangeText={(text) => {
            setUnlockPassword(text);
            if (unlockError) {
                setUnlockError("");
            }
        }} placeholder={passkeyUnlockPlaceholder} error={unlockError} autoComplete="password" returnKeyType="done" onSubmitEditing={handleUnlockAfterPasskey} accessibilityLabel="Master password" testID="login-passkey-unlock-input"/>
        </View>
        <DialogFooter style={{ justifyContent: "center" }}>
          <DialogButton variant="primary" onPress={withClickTracking("auth.login.action", isLoading ? () => { } : handleUnlockAfterPasskey)} accessibilityLabel="Unlock" accessibilityRole="button">
            {isLoading ? "Unlocking…" : "Unlock"}
          </DialogButton>
        </DialogFooter>
      </Dialog>
    </>);
}
export default LoginScreen;
//# sourceMappingURL=LoginScreen.jsx.map