import { useEffect, useState } from 'react';
import {
  Alert,
  Anchor,
  Badge,
  Button,
  Checkbox,
  Divider,
  PasswordInput,
  SimpleGrid,
  Stack,
  Text,
  TextInput,
  Title,
  UnstyledButton,
} from '@mantine/core';
import {
  IconAlertTriangle,
  IconArrowLeft,
  IconArrowRight,
  IconAt,
  IconId,
  IconLock,
  IconMail,
  IconRosetteDiscountCheck,
  IconUser,
} from '@tabler/icons-react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { useNavigate, Link } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { useApiMutation } from '@ema-platform/api';
import { useErrorHandler, passwordSchema, PasswordRequirements, phoneNumber, PhoneInput } from '@ema-platform/ui';
import { AuthShell } from '../components/AuthShell';
import { loginSuccess, setUser } from '../store/auth.slice';
import type { AuthUser } from '../types/auth.types';
import { useAuthConfig } from '../AuthConfig';
import { faydaSession, type FaydaResult } from '../utils/fayda-session';

interface SignupPayload {
  email: string;
  username: string;
  phoneNumber: string;
  userType: string;
  name: {
    am: string;
    en: string;
  };
  password: string;
  confirmPassword: string;
}

export function SignupPage() {
  const navigate = useNavigate();
  const dispatch = useDispatch();
  const { t } = useTranslation();
  const { appName, loginRedirectPath } = useAuthConfig();
  const [agreed, setAgreed] = useState(false);
  const [serverError, setServerError] = useState<string | null>(null);
  const { handleError } = useErrorHandler();
  const [signupTrigger, { isLoading: loading }] = useApiMutation<{
    token: string;
    refreshToken: string;
    isPhoneNumberVerified: boolean;
  }>();
  const [meTrigger] = useApiMutation<AuthUser>();

  // Fayda is optional: the form below works exactly as before without it.
  const [fayda, setFayda] = useState<FaydaResult | null>(() => faydaSession.peekResult());
  const [faydaStarting, setFaydaStarting] = useState(false);
  const [startTrigger] = useApiMutation<{
    authorizationUrl: string;
    state: string;
    transactionToken: string;
    expiresIn: number;
  }>();
  const [linkTrigger] = useApiMutation<{ phoneNumberVerified?: boolean }>();

  const verified = (field: string) => fayda?.verifiedFields.includes(field) ?? false;
  const conflicted = (field: string) => fayda?.conflicts.includes(field) ?? false;

  /**
   * Per-field provenance, so it is obvious which values came from Fayda and
   * which are still the applicant's to supply. Verified fields stay editable —
   * a conflicting email has to be changeable for the form to be completable at
   * all.
   */
  const faydaMark = (field: string): { description?: React.ReactNode } => {
    if (conflicted(field)) {
      return {
        // component="span" on these badges: the description slot renders
        // inside a <p>, where Badge's default <div> is invalid HTML.
        description: (
          <Badge component="span" size="xs" variant="light" color="orange">
            {t('fayda.fieldConflict', 'Already used by another account')}
          </Badge>
        ),
      };
    }
    if (verified(field)) {
      return {
        description: (
          <Badge
            component="span"
            size="xs"
            variant="light"
            color="teal"
            leftSection={<IconRosetteDiscountCheck size={11} />}
          >
            {t('fayda.fieldVerified', 'From Fayda')}
          </Badge>
        ),
      };
    }
    return {};
  };

  const handleBack = () => {
    if (window.history.length > 1) {
      navigate(-1);
    } else {
      navigate('/');
    }
  };

  // Order matches passwordRules' default list: length, lowercase, uppercase,
  // number, special character. Shared between the zod schema (field error)
  // and the live checklist below, so both agree on the wording.
  const passwordRuleLabels = [
    t('signup.passwordRule.minLength', { min: 8, defaultValue: 'At least {{min}} characters' }),
    t('signup.passwordRule.lowercase', 'One lowercase letter'),
    t('signup.passwordRule.uppercase', 'One uppercase letter'),
    t('signup.passwordRule.number', 'One number'),
    t('signup.passwordRule.special', 'One special character'),
  ];

  // Built inside the component (not module scope) so validation messages
  // pick up the active language — same pattern as ProfilePage's forms.
  const schema = z
    .object({
      email: z.string().email(),
      username: z.string().min(3, { message: t('signup.usernameMinLength', 'Username must be at least 3 characters') }),
      phoneNumber,
      userType: z.literal('individual'),
      nameEn: z
        .string()
        .min(1, { message: t('signup.nameEnRequired', 'Name (English) is required') })
        .refine((v) => v.trim().split(/\s+/).length >= 3, {
          message: t('signup.nameEnFullNameRequired', 'Please enter your full name (first, middle, and last)'),
        }),
      nameAm: z.string().optional(),
      password: passwordSchema(8, passwordRuleLabels),
      confirmPassword: z.string().min(1, { message: t('signup.confirmPasswordRequired', 'Confirm your password') }),
    })
    .refine((data) => data.password === data.confirmPassword, {
      message: t('signup.passwordsDontMatch', 'Passwords do not match'),
      path: ['confirmPassword'],
    });

  type FormValues = z.infer<typeof schema>;

  const {
    register,
    handleSubmit,
    watch,
    setValue,
    trigger,
    formState: { errors },
  } = useForm<FormValues>({
    resolver: zodResolver(schema),
    defaultValues: { userType: 'individual' },
  });

  // Fills what Fayda vouched for and leaves the rest — username and password
  // are always the applicant's to choose, and Fayda supplies neither.
  useEffect(() => {
    if (!fayda) return;
    const { email, phoneNumber: phone, nameEn, nameAm } = fayda.identity;
    if (email) setValue('email', email);
    if (phone) setValue('phoneNumber', phone);
    if (nameEn) setValue('nameEn', nameEn);
    if (nameAm) setValue('nameAm', nameAm);
  }, [fayda, setValue]);

  const startFayda = async () => {
    setServerError(null);
    setFaydaStarting(true);
    try {
      // Same endpoint the registration itself uses; `start` only opens the
      // attempt and hands back where to send the user.
      const { authorizationUrl, transactionToken, state } = await startTrigger({
        url: '/auth/register-with-fayda',
        method: 'POST',
        body: { action: 'start' },
      }).unwrap();

      faydaSession.saveRequest({ transactionToken, state });
      window.location.assign(authorizationUrl);
    } catch (err: unknown) {
      setFaydaStarting(false);
      setServerError(handleError(err));
    }
  };

  const clearFayda = () => {
    faydaSession.clearResult();
    setFayda(null);
  };

  const onSubmit = async (values: FormValues) => {
    try {
      const payload: SignupPayload = {
        email: values.email,
        username: values.username,
        phoneNumber: values.phoneNumber,
        userType: values.userType,
        name: { en: values.nameEn, am: values.nameAm ?? '' },
        password: values.password,
        confirmPassword: values.confirmPassword,
      };

      const data = await signupTrigger({
        url: '/auth/signup-with-pwd',
        method: 'POST',
        body: payload,
      }).unwrap();

      dispatch(
        loginSuccess({
          token: data.token,
          refreshToken: data.refreshToken,
          isPhoneNumberVerified: data.isPhoneNumberVerified,
        }),
      );

      const me = await meTrigger({ url: '/auth/me', method: 'GET' }).unwrap();
      dispatch(setUser(me));

      // Records the Fayda-verified identity on the new account: marks the
      // phone verified when it is the one Fayda vouched for, and fills the
      // still-empty profile fields. Best-effort — the account already works,
      // and the token can be presented again on a retry.
      let faydaPhoneVerified = false;
      if (fayda?.verificationToken) {
        try {
          const applied = await linkTrigger({
            url: '/profiles/me/fayda',
            method: 'POST',
            body: { verificationToken: fayda.verificationToken },
          }).unwrap();
          faydaPhoneVerified = Boolean(applied?.phoneNumberVerified);
        } catch {
          /* deliberately ignored — signup already succeeded */
        }
      }
      faydaSession.clearResult();

      // Fayda already verified this exact number via its own OTP; asking for
      // a second OTP on the same number is theatre.
      if (data.isPhoneNumberVerified || faydaPhoneVerified) {
        navigate(loginRedirectPath);
      } else {
        navigate('/otp-verify', {
          state: {
            email: values.email,
            phoneNumber: values.phoneNumber,
          },
        });
      }
    } catch (err: unknown) {
      setServerError(handleError(err));
    }
  };

  return (
    <AuthShell
      brandTitle={t('signup.brandTitle', { appName, defaultValue: "Join {{appName}}'s community." })}
      brandSubtitle={t('signup.brandSubtitle', {
        appName,
        defaultValue: 'Create your account to access {{appName}} features.',
      })}
    >
      <Stack gap="lg">
        <UnstyledButton
          onClick={handleBack}
          style={{
            display: 'inline-flex',
            alignItems: 'center',
            gap: 6,
            fontSize: 14,
            fontWeight: 600,
            color: 'var(--mantine-color-dimmed)',
            cursor: 'pointer',
            width: 'fit-content',
            transition: 'all 150ms ease',
          }}
          onMouseEnter={(e) => {
            e.currentTarget.style.color = 'var(--mantine-primary-color-filled)';
            e.currentTarget.style.transform = 'translateX(-3px)';
          }}
          onMouseLeave={(e) => {
            e.currentTarget.style.color = 'var(--mantine-color-dimmed)';
            e.currentTarget.style.transform = 'translateX(0)';
          }}
        >
          <IconArrowLeft size={18} />
          {t('common.back', 'Back')}
        </UnstyledButton>

        <div>
          <Title order={2} fz={30}>
            {t('signup.title', 'Create account')}
          </Title>
          <Text c="dimmed" mt={6}>
            {t('signup.subtitle', 'It only takes a minute to get started.')}
          </Text>
        </div>

        {serverError && (
          <Alert variant="light" color="red" withCloseButton onClose={() => setServerError(null)}>
            {serverError}
          </Alert>
        )}

        {fayda ? (
          <Alert
            variant="light"
            color="teal"
            icon={<IconRosetteDiscountCheck size={18} />}
            title={t('fayda.verifiedTitle', 'Verified with Fayda')}
          >
            <Stack gap="xs">
              <Text size="sm">
                {t(
                  'fayda.verifiedBody',
                  'We filled in the details Fayda confirmed. Please complete the remaining fields.',
                )}
              </Text>
              <Anchor size="sm" component="button" type="button" onClick={clearFayda}>
                {t('fayda.discard', 'Clear these details and fill the form myself')}
              </Anchor>
            </Stack>
          </Alert>
        ) : (
          <>
            <Button
              variant="default"
              size="md"
              fullWidth
              loading={faydaStarting}
              leftSection={<IconId size={18} />}
              onClick={startFayda}
            >
              {t('fayda.continueWith', 'Continue with Fayda')}
            </Button>
            <Divider
              label={t('fayda.orFillManually', 'or fill in your details')}
              labelPosition="center"
            />
          </>
        )}

        {fayda && fayda.conflicts.length > 0 && (
          <Alert variant="light" color="orange" icon={<IconAlertTriangle size={18} />}>
            {t(
              'fayda.conflictBody',
              'Some verified details already belong to another account. Change the highlighted fields, or sign in instead.',
            )}
          </Alert>
        )}

        <form onSubmit={handleSubmit(onSubmit)}>
          <Stack gap="md">
            <SimpleGrid cols={{ base: 1, xs: 2 }} spacing="md">
              <TextInput
                label={t('signup.nameEnLabel', 'Full name (English)')}
                placeholder={t('signup.nameEnPlaceholder', 'Abebe Bekele')}
                leftSection={<IconUser size={18} />}
                error={errors.nameEn?.message}
                {...faydaMark('nameEn')}
                {...register('nameEn')}
              />
              <TextInput
                label={t('signup.nameAmLabel', 'Name (Amharic)')}
                placeholder={t('signup.nameAmPlaceholder', 'ስም')}
                leftSection={<IconUser size={18} />}
                error={errors.nameAm?.message}
                {...faydaMark('nameAm')}
                {...register('nameAm')}
              />
            </SimpleGrid>

            <SimpleGrid cols={{ base: 1, xs: 2 }} spacing="md">
              <TextInput
                label={t('signup.emailLabel', 'Email address')}
                placeholder={t('signup.emailPlaceholder', 'you@example.com')}
                leftSection={<IconMail size={18} />}
                error={errors.email?.message}
                {...faydaMark('email')}
                {...register('email')}
              />
              <TextInput
                label={t('signup.usernameLabel', 'Username')}
                placeholder={t('signup.usernamePlaceholder', 'Choose a username')}
                leftSection={<IconAt size={18} />}
                error={errors.username?.message}
                {...register('username')}
              />
            </SimpleGrid>

            <PhoneInput
              label={t('signup.phoneLabel', 'Phone number')}
              placeholder={t('signup.phonePlaceholder', '9XX XXX XXX')}
              value={watch('phoneNumber') || ''}
              onChange={(val) => setValue('phoneNumber', val, { shouldValidate: !!errors.phoneNumber })}
              onBlur={() => trigger('phoneNumber')}
              error={errors.phoneNumber?.message}
              {...faydaMark('phoneNumber')}
            />

            <SimpleGrid cols={{ base: 1, xs: 2 }} spacing="md">
              <div>
                <PasswordInput
                  label={t('signup.passwordLabel', 'Password')}
                  placeholder={t('signup.passwordPlaceholder', 'At least 8 characters')}
                  leftSection={<IconLock size={18} />}
                  error={errors.password?.message}
                  {...register('password')}
                />
                <PasswordRequirements
                  password={watch('password') ?? ''}
                  minLength={8}
                  labels={passwordRuleLabels}
                />
              </div>
              <PasswordInput
                label={t('signup.confirmPasswordLabel', 'Confirm password')}
                placeholder={t('signup.confirmPasswordPlaceholder', 'Re-enter password')}
                leftSection={<IconLock size={18} />}
                error={errors.confirmPassword?.message}
                {...register('confirmPassword')}
              />
            </SimpleGrid>

            <Checkbox
              size="sm"
              checked={agreed}
              onChange={(e) => setAgreed(e.currentTarget.checked)}
              label={
                <Text size="sm">
                  {t('signup.agreeToThe', 'I agree to the ')}
                  <Anchor
                    size="sm"
                    fw={600}
                    onClick={(e) => e.preventDefault()}
                  >
                    {t('signup.termsAndPrivacy', 'Terms & Privacy Policy')}
                  </Anchor>
                </Text>
              }
            />

            <Button
              type="submit"
              loading={loading}
              disabled={!agreed}
              fullWidth
              size="md"
              rightSection={<IconArrowRight size={18} />}
            >
              {t('signup.createAccount', 'Create account')}
            </Button>
          </Stack>
        </form>

        <Text ta="center" size="sm" c="dimmed">
          {t('signup.haveAccount', 'Already have an account? ')}
          <Anchor component={Link} to="/login" fw={700}>
            {t('signup.signIn', 'Sign in')}
          </Anchor>
        </Text>
      </Stack>
    </AuthShell>
  );
}
