import React, { useEffect, useMemo, useState } from "react";
import { Platform, ScrollView, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { useLocalSearchParams } from "expo-router";
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 { 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 { Input, PasswordInput } from "../../components/input";
import PasswordStrengthMeter from "../../components/auth/PasswordStrengthMeter";
import PwnedPasswordWarning from "../../components/auth/PwnedPasswordWarning";
import ShakeField from "../../forms/shake-field";
import FormError from "../../forms/form-error";
import { useShakeAnimation } from "../../hooks/useShakeAnimation";
import AuthService from "../../services/auth.service";
import { checkPasswordPwned, debounce, getPwnedMessage } from "../../services/hibp";
import { getCachedPolicy } from "../../services/hibp-cache";
import { useAppConfig } from "../../context/AppConfigContext";
import { getErrorMessage } from "../../utils/error";
import { ApiError } from "../../utils/http-error";
const DEFAULT_POLICY = { enabled: false, enforce: false, block_threshold: 0 };
export function ResetPasswordScreen({ encryptionWarning } = {}) {
    const params = useLocalSearchParams();
    const router = useNavigation();
    const { routes } = useAppConfig();
    const [step, setStep] = useState("code");
    const [code, setCode] = useState("");
    const [newPassword, setNewPassword] = useState("");
    const [confirmPassword, setConfirmPassword] = useState("");
    const [errors, setErrors] = useState({});
    const [isLoading, setIsLoading] = useState(false);
    const [pwnedInfo, setPwnedInfo] = useState(null);
    const { colorScheme } = useColorScheme();
    const isDark = colorScheme === "dark";
    const codeShake = useShakeAnimation();
    const passwordShake = useShakeAnimation();
    const confirmShake = useShakeAnimation();
    const firstError = errors.code ?? errors.password ?? errors.confirmPassword;
    const checkPasswordDebounced = useMemo(() => debounce(async (pass) => {
        const policy = getCachedPolicy() ?? DEFAULT_POLICY;
        if (!policy.enabled || pass.length < 8) {
            setPwnedInfo(null);
            return;
        }
        const result = await checkPasswordPwned(pass);
        if (result.isPwned) {
            const message = getPwnedMessage(result);
            setPwnedInfo({ ...message, count: result.count });
        }
        else {
            setPwnedInfo(null);
        }
    }, 1000), []);
    useEffect(() => () => checkPasswordDebounced.cancel(), [checkPasswordDebounced]);
    useEffect(() => {
        let urlCode = null;
        if (params.code) {
            urlCode = params.code;
        }
        else if (Platform.OS === "web") {
            const urlParams = new URLSearchParams(window.location.search);
            urlCode = urlParams.get("code");
        }
        if (urlCode) {
            const captured = urlCode;
            AuthService.validateResetCode(captured.trim())
                .then(() => {
                setCode(captured);
                setStep("password");
            })
                .catch(() => setErrors({ code: "Invalid or expired code" }));
        }
    }, [params.code]);
    const handleCodeSubmit = async () => {
        // Re-entry guard against double-submit before isLoading lands.
        if (isLoading) {
            return;
        }
        if (!code.trim()) {
            setErrors({ code: "Reset code is required" });
            codeShake.shake();
            await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
            return;
        }
        setIsLoading(true);
        try {
            await AuthService.validateResetCode(code.trim());
            setErrors({});
            setStep("password");
        }
        catch (e) {
            setErrors({ code: getErrorMessage(e, "Invalid or expired code") });
            codeShake.shake();
            await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
        }
        finally {
            setIsLoading(false);
        }
    };
    const handlePasswordSubmit = async () => {
        // Re-entry guard. The HIBP probe below is itself a network
        // round-trip; without this guard, a double-submit before
        // `setIsLoading(true)` fires would double-issue HIBP fetch + the
        // resetPassword POST.
        if (isLoading) {
            return;
        }
        const newErrors = {};
        if (!newPassword) {
            newErrors.password = "Password is required";
        }
        else if (newPassword.length < 8) {
            newErrors.password = "Password must be at least 8 characters";
        }
        if (!confirmPassword) {
            newErrors.confirmPassword = "Please confirm your password";
        }
        else if (newPassword !== confirmPassword) {
            newErrors.confirmPassword = "Passwords do not match";
        }
        if (Object.keys(newErrors).length > 0) {
            setErrors(newErrors);
            if (newErrors.password) {
                passwordShake.shake();
            }
            if (newErrors.confirmPassword) {
                confirmShake.shake();
            }
            await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
            return;
        }
        setErrors({});
        const hibpPolicy = getCachedPolicy() ?? DEFAULT_POLICY;
        if (hibpPolicy.enabled) {
            const pwnedResult = await checkPasswordPwned(newPassword);
            if (pwnedResult.isPwned) {
                const message = getPwnedMessage(pwnedResult);
                setPwnedInfo({ ...message, count: pwnedResult.count });
                if (hibpPolicy.enforce && pwnedResult.count > hibpPolicy.block_threshold) {
                    await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
                    passwordShake.shake();
                    confirmShake.shake();
                    return;
                }
            }
            else {
                setPwnedInfo(null);
            }
        }
        setIsLoading(true);
        try {
            await AuthService.resetPassword(code, newPassword);
            await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
            router.replace(routes.login);
        }
        catch (e) {
            const msg = getErrorMessage(e, "Failed to reset password");
            // Branch on the server's machine-readable `error.code` rather
            // than English-substring matching the message — i18n / wording
            // tweaks on the server would silently break the old behaviour.
            const code = e instanceof ApiError ? e.code : undefined;
            if (code === "INVALID_CODE") {
                setErrors({ code: msg });
                codeShake.shake();
                setStep("code");
            }
            else {
                setErrors({ password: msg });
                passwordShake.shake();
            }
            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" contentContainerClassName="flex-1 items-center justify-center p-6" keyboardShouldPersistTaps="handled">
        <View className="mt-4 self-start absolute top-4 left-6">
          <IconButton variant="ghost" telemetryId="auth.reset_password.step" onPress={() => (step === "password" ? setStep("code") : router.back())} accessibilityLabel="Go back" accessibilityRole="button">
            <Ionicons name="arrow-back" size={24} color={isDark ? "#FFFFFF" : "#000000"}/>
          </IconButton>
        </View>

        <Card className="w-full max-w-[360px]">
          {step === "code" ? (<>
              <CardHeader>
                <CardTitle>Reset password</CardTitle>
                <CardDescription>Enter the reset code from your email</CardDescription>
              </CardHeader>

              <CardContent className="gap-3">
                <ShakeField shake={codeShake} label="Reset Code">
                  <Input value={code} onChangeText={(text) => {
                setCode(text);
                if (errors.code) {
                    setErrors({ ...errors, code: undefined });
                }
            }} invalid={!!errors.code} placeholder="Paste your reset code" autoCapitalize="none" autoFocus returnKeyType="done" onSubmitEditing={handleCodeSubmit} accessibilityLabel="Reset code"/>
                </ShakeField>
              </CardContent>

              <CardFooter className="flex-col items-stretch gap-3 border-t-0">
                <FormError message={firstError}/>

                <Button className="w-full" telemetryId="auth.reset_password.code_submit" onPress={handleCodeSubmit} isLoading={isLoading} accessibilityLabel="Continue" accessibilityRole="button">
                  Continue
                </Button>

                <MutedText className="text-center text-sm">
                  Back to{" "}
                  <Text className="text-sm text-foreground underline" onPress={withClickTracking("auth.reset_password.push", () => router.push(routes.login))} accessibilityRole="link" accessibilityLabel="Sign in">
                    Sign In
                  </Text>
                </MutedText>
              </CardFooter>
            </>) : (<>
              <CardHeader>
                <CardTitle>New password</CardTitle>
                <CardDescription>
                  Choose a strong password for your account.
                  {encryptionWarning ? `\n${encryptionWarning}` : ""}
                </CardDescription>
              </CardHeader>

              <CardContent className="gap-3">
                <View>
                  <ShakeField shake={passwordShake} label="New Password">
                    <PasswordInput value={newPassword} onChangeText={(text) => {
                setNewPassword(text);
                if (errors.password) {
                    setErrors({ ...errors, password: undefined });
                }
                checkPasswordDebounced(text);
            }} invalid={!!errors.password} placeholder="New password" autoFocus accessibilityLabel="New password"/>
                  </ShakeField>

                  <PasswordStrengthMeter password={newPassword}/>
                  <PwnedPasswordWarning pwnedInfo={pwnedInfo}/>
                </View>

                <ShakeField shake={confirmShake} label="Confirm Password">
                  <PasswordInput value={confirmPassword} onChangeText={(text) => {
                setConfirmPassword(text);
                if (errors.confirmPassword) {
                    setErrors({ ...errors, confirmPassword: undefined });
                }
            }} invalid={!!errors.confirmPassword} placeholder="Confirm new password" returnKeyType="done" onSubmitEditing={handlePasswordSubmit} accessibilityLabel="Confirm new password"/>
                </ShakeField>
              </CardContent>

              <CardFooter className="flex-col items-stretch gap-3 border-t-0">
                <FormError message={firstError}/>

                <Button className="w-full" telemetryId="auth.reset_password.password_submit" onPress={handlePasswordSubmit} isLoading={isLoading} accessibilityLabel="Reset password" accessibilityRole="button">
                  Reset Password
                </Button>

                <MutedText className="text-center text-sm">
                  <Text className="text-sm text-foreground underline" onPress={withClickTracking("auth.reset_password.step_2", () => setStep("code"))} accessibilityRole="link" accessibilityLabel="Use a different code">
                    Use a different code
                  </Text>
                </MutedText>
              </CardFooter>
            </>)}
        </Card>
      </ScrollView>
    </SafeAreaView>);
}
export default ResetPasswordScreen;
//# sourceMappingURL=ResetPasswordScreen.jsx.map