'use client';

import { CountryPicker } from '@/components/country-picker';
import { Input, Label } from '@/components/input';
import { Logo } from '@/components/logo';
import { Button } from '@/components/ui/button';
import { apiFetch, apiUrl } from '@/lib/api';
import Link from 'next/link';
import { useEffect, useState } from 'react';

const ERROR_COPY: Record<string, string> = {
  google_failed: 'Google sign-in could not be completed. Please try again.',
  google_state: 'Google sign-in expired or was interrupted. Please try again.',
  github_failed: 'GitHub sign-in could not be completed. Please try again.',
  github_state: 'GitHub sign-in expired or was interrupted. Please try again.',
  invalid_phone: 'That phone number does not look right. Check the country and number.',
  rate_limited: 'Too many requests. Wait a few minutes and try again.',
  sms_request_failed: 'Could not send the SMS. Check the number and try again.',
  invalid_or_expired_code: 'That code has expired. Request a new one.',
  invalid_code: 'Wrong code. Check the SMS and try again.',
  too_many_attempts: 'Too many wrong attempts. Request a new code.',
  sms_verify_failed: 'Could not verify the code. Try again.',
};

// Country dial codes for the phone-login picker. ~150 entries — every country
// with a non-trivial diaspora. Sorted alphabetically by name. Switzerland is
// the default (Swiss-built product, Swiss Twilio sender number).
const COUNTRIES: { code: string; name: string; dial: string }[] = [
  { code: 'AL', name: 'Albania', dial: '+355' },
  { code: 'DZ', name: 'Algeria', dial: '+213' },
  { code: 'AD', name: 'Andorra', dial: '+376' },
  { code: 'AO', name: 'Angola', dial: '+244' },
  { code: 'AR', name: 'Argentina', dial: '+54' },
  { code: 'AM', name: 'Armenia', dial: '+374' },
  { code: 'AW', name: 'Aruba', dial: '+297' },
  { code: 'AU', name: 'Australia', dial: '+61' },
  { code: 'AT', name: 'Austria', dial: '+43' },
  { code: 'AZ', name: 'Azerbaijan', dial: '+994' },
  { code: 'BS', name: 'Bahamas', dial: '+1' },
  { code: 'BH', name: 'Bahrain', dial: '+973' },
  { code: 'BD', name: 'Bangladesh', dial: '+880' },
  { code: 'BB', name: 'Barbados', dial: '+1' },
  { code: 'BY', name: 'Belarus', dial: '+375' },
  { code: 'BE', name: 'Belgium', dial: '+32' },
  { code: 'BZ', name: 'Belize', dial: '+501' },
  { code: 'BJ', name: 'Benin', dial: '+229' },
  { code: 'BM', name: 'Bermuda', dial: '+1' },
  { code: 'BT', name: 'Bhutan', dial: '+975' },
  { code: 'BO', name: 'Bolivia', dial: '+591' },
  { code: 'BA', name: 'Bosnia & Herzegovina', dial: '+387' },
  { code: 'BW', name: 'Botswana', dial: '+267' },
  { code: 'BR', name: 'Brazil', dial: '+55' },
  { code: 'BN', name: 'Brunei', dial: '+673' },
  { code: 'BG', name: 'Bulgaria', dial: '+359' },
  { code: 'BF', name: 'Burkina Faso', dial: '+226' },
  { code: 'KH', name: 'Cambodia', dial: '+855' },
  { code: 'CM', name: 'Cameroon', dial: '+237' },
  { code: 'CA', name: 'Canada', dial: '+1' },
  { code: 'CV', name: 'Cape Verde', dial: '+238' },
  { code: 'KY', name: 'Cayman Islands', dial: '+1' },
  { code: 'CL', name: 'Chile', dial: '+56' },
  { code: 'CN', name: 'China', dial: '+86' },
  { code: 'CO', name: 'Colombia', dial: '+57' },
  { code: 'CR', name: 'Costa Rica', dial: '+506' },
  { code: 'HR', name: 'Croatia', dial: '+385' },
  { code: 'CY', name: 'Cyprus', dial: '+357' },
  { code: 'CZ', name: 'Czechia', dial: '+420' },
  { code: 'DK', name: 'Denmark', dial: '+45' },
  { code: 'DO', name: 'Dominican Republic', dial: '+1' },
  { code: 'EC', name: 'Ecuador', dial: '+593' },
  { code: 'EG', name: 'Egypt', dial: '+20' },
  { code: 'SV', name: 'El Salvador', dial: '+503' },
  { code: 'EE', name: 'Estonia', dial: '+372' },
  { code: 'ET', name: 'Ethiopia', dial: '+251' },
  { code: 'FJ', name: 'Fiji', dial: '+679' },
  { code: 'FI', name: 'Finland', dial: '+358' },
  { code: 'FR', name: 'France', dial: '+33' },
  { code: 'GE', name: 'Georgia', dial: '+995' },
  { code: 'DE', name: 'Germany', dial: '+49' },
  { code: 'GH', name: 'Ghana', dial: '+233' },
  { code: 'GR', name: 'Greece', dial: '+30' },
  { code: 'GT', name: 'Guatemala', dial: '+502' },
  { code: 'HN', name: 'Honduras', dial: '+504' },
  { code: 'HK', name: 'Hong Kong', dial: '+852' },
  { code: 'HU', name: 'Hungary', dial: '+36' },
  { code: 'IS', name: 'Iceland', dial: '+354' },
  { code: 'IN', name: 'India', dial: '+91' },
  { code: 'ID', name: 'Indonesia', dial: '+62' },
  { code: 'IR', name: 'Iran', dial: '+98' },
  { code: 'IQ', name: 'Iraq', dial: '+964' },
  { code: 'IE', name: 'Ireland', dial: '+353' },
  { code: 'IL', name: 'Israel', dial: '+972' },
  { code: 'IT', name: 'Italy', dial: '+39' },
  { code: 'CI', name: 'Ivory Coast', dial: '+225' },
  { code: 'JM', name: 'Jamaica', dial: '+1' },
  { code: 'JP', name: 'Japan', dial: '+81' },
  { code: 'JO', name: 'Jordan', dial: '+962' },
  { code: 'KZ', name: 'Kazakhstan', dial: '+7' },
  { code: 'KE', name: 'Kenya', dial: '+254' },
  { code: 'XK', name: 'Kosovo', dial: '+383' },
  { code: 'KW', name: 'Kuwait', dial: '+965' },
  { code: 'KG', name: 'Kyrgyzstan', dial: '+996' },
  { code: 'LA', name: 'Laos', dial: '+856' },
  { code: 'LV', name: 'Latvia', dial: '+371' },
  { code: 'LB', name: 'Lebanon', dial: '+961' },
  { code: 'LY', name: 'Libya', dial: '+218' },
  { code: 'LI', name: 'Liechtenstein', dial: '+423' },
  { code: 'LT', name: 'Lithuania', dial: '+370' },
  { code: 'LU', name: 'Luxembourg', dial: '+352' },
  { code: 'MO', name: 'Macau', dial: '+853' },
  { code: 'MK', name: 'North Macedonia', dial: '+389' },
  { code: 'MG', name: 'Madagascar', dial: '+261' },
  { code: 'MY', name: 'Malaysia', dial: '+60' },
  { code: 'MV', name: 'Maldives', dial: '+960' },
  { code: 'MT', name: 'Malta', dial: '+356' },
  { code: 'MX', name: 'Mexico', dial: '+52' },
  { code: 'MD', name: 'Moldova', dial: '+373' },
  { code: 'MC', name: 'Monaco', dial: '+377' },
  { code: 'MN', name: 'Mongolia', dial: '+976' },
  { code: 'ME', name: 'Montenegro', dial: '+382' },
  { code: 'MA', name: 'Morocco', dial: '+212' },
  { code: 'MZ', name: 'Mozambique', dial: '+258' },
  { code: 'MM', name: 'Myanmar', dial: '+95' },
  { code: 'NA', name: 'Namibia', dial: '+264' },
  { code: 'NP', name: 'Nepal', dial: '+977' },
  { code: 'NL', name: 'Netherlands', dial: '+31' },
  { code: 'NZ', name: 'New Zealand', dial: '+64' },
  { code: 'NI', name: 'Nicaragua', dial: '+505' },
  { code: 'NG', name: 'Nigeria', dial: '+234' },
  { code: 'NO', name: 'Norway', dial: '+47' },
  { code: 'OM', name: 'Oman', dial: '+968' },
  { code: 'PK', name: 'Pakistan', dial: '+92' },
  { code: 'PS', name: 'Palestine', dial: '+970' },
  { code: 'PA', name: 'Panama', dial: '+507' },
  { code: 'PG', name: 'Papua New Guinea', dial: '+675' },
  { code: 'PY', name: 'Paraguay', dial: '+595' },
  { code: 'PE', name: 'Peru', dial: '+51' },
  { code: 'PH', name: 'Philippines', dial: '+63' },
  { code: 'PL', name: 'Poland', dial: '+48' },
  { code: 'PT', name: 'Portugal', dial: '+351' },
  { code: 'PR', name: 'Puerto Rico', dial: '+1' },
  { code: 'QA', name: 'Qatar', dial: '+974' },
  { code: 'RO', name: 'Romania', dial: '+40' },
  { code: 'RU', name: 'Russia', dial: '+7' },
  { code: 'RW', name: 'Rwanda', dial: '+250' },
  { code: 'SM', name: 'San Marino', dial: '+378' },
  { code: 'SA', name: 'Saudi Arabia', dial: '+966' },
  { code: 'SN', name: 'Senegal', dial: '+221' },
  { code: 'RS', name: 'Serbia', dial: '+381' },
  { code: 'SG', name: 'Singapore', dial: '+65' },
  { code: 'SK', name: 'Slovakia', dial: '+421' },
  { code: 'SI', name: 'Slovenia', dial: '+386' },
  { code: 'SO', name: 'Somalia', dial: '+252' },
  { code: 'ZA', name: 'South Africa', dial: '+27' },
  { code: 'KR', name: 'South Korea', dial: '+82' },
  { code: 'ES', name: 'Spain', dial: '+34' },
  { code: 'LK', name: 'Sri Lanka', dial: '+94' },
  { code: 'SD', name: 'Sudan', dial: '+249' },
  { code: 'SE', name: 'Sweden', dial: '+46' },
  { code: 'CH', name: 'Switzerland', dial: '+41' },
  { code: 'SY', name: 'Syria', dial: '+963' },
  { code: 'TW', name: 'Taiwan', dial: '+886' },
  { code: 'TJ', name: 'Tajikistan', dial: '+992' },
  { code: 'TZ', name: 'Tanzania', dial: '+255' },
  { code: 'TH', name: 'Thailand', dial: '+66' },
  { code: 'TT', name: 'Trinidad & Tobago', dial: '+1' },
  { code: 'TN', name: 'Tunisia', dial: '+216' },
  { code: 'TR', name: 'Turkey', dial: '+90' },
  { code: 'TM', name: 'Turkmenistan', dial: '+993' },
  { code: 'UG', name: 'Uganda', dial: '+256' },
  { code: 'UA', name: 'Ukraine', dial: '+380' },
  { code: 'AE', name: 'United Arab Emirates', dial: '+971' },
  { code: 'GB', name: 'United Kingdom', dial: '+44' },
  { code: 'US', name: 'United States', dial: '+1' },
  { code: 'UY', name: 'Uruguay', dial: '+598' },
  { code: 'UZ', name: 'Uzbekistan', dial: '+998' },
  { code: 'VE', name: 'Venezuela', dial: '+58' },
  { code: 'VN', name: 'Vietnam', dial: '+84' },
  { code: 'YE', name: 'Yemen', dial: '+967' },
  { code: 'ZM', name: 'Zambia', dial: '+260' },
  { code: 'ZW', name: 'Zimbabwe', dial: '+263' },
];

function dialFor(code: string): string {
  return COUNTRIES.find((c) => c.code === code)?.dial ?? '+41';
}

/** Combine a dial code and a locally-typed number into strict E.164. */
function toE164(dial: string, local: string): string {
  const digits = local.replace(/\D/g, '').replace(/^0+/, '');
  return dial + digits;
}

function errCode(err: unknown): string {
  const detail = (err as { detail?: { error?: string } }).detail;
  return detail?.error ?? (err as Error).message ?? 'unknown';
}

export default function LoginPage() {
  const [providers, setProviders] = useState({
    google: false,
    github: false,
    sms: false,
    email: false,
  });
  // Phone sign-in is deliberately collapsed behind a text link whenever any
  // other provider (OAuth or email) is available — a developer evaluating the
  // product should never see a phone-number field as the front door. It only
  // renders expanded when SMS is the sole configured provider.
  const [phoneOpen, setPhoneOpen] = useState(false);
  const [providersState, setProvidersState] = useState<'loading' | 'ready' | 'failed'>('loading');
  const [error, setError] = useState<string | null>(null);

  // Email magic-link
  const [email, setEmail] = useState('');
  const [emailState, setEmailState] = useState<'idle' | 'sending' | 'sent'>('idle');

  // SMS one-time code
  const [country, setCountry] = useState('CH');
  const [phoneLocal, setPhoneLocal] = useState('');
  const [sentTo, setSentTo] = useState('');
  const [code, setCode] = useState('');
  const [smsStep, setSmsStep] = useState<'phone' | 'code'>('phone');
  const [smsBusy, setSmsBusy] = useState(false);

  useEffect(() => {
    apiFetch<{ google: boolean; github: boolean; sms: boolean; email: boolean }>(
      '/v1/auth/providers',
    )
      .then((p) => {
        setProviders(p);
        setProvidersState('ready');
        // SMS as the only provider → show the phone form directly (no point
        // hiding the sole sign-in method behind a toggle).
        if (p.sms && !p.email && !p.google && !p.github) setPhoneOpen(true);
      })
      .catch(() => {
        // If the providers lookup fails, the page must not strand the visitor
        // with zero sign-in methods — fall back to SMS + phone form expanded
        // (the one flow that has no provider-specific client config) and say
        // why the other options are missing.
        setProviders({ google: false, github: false, sms: true, email: false });
        setPhoneOpen(true);
        setProvidersState('failed');
        setError('Some sign-in options could not be loaded. Reload the page to try again.');
      });
    const err = new URLSearchParams(window.location.search).get('error');
    if (err) setError(ERROR_COPY[err] ?? 'Sign-in failed. Please try again.');
  }, []);

  async function sendMagicLink(e: React.FormEvent) {
    e.preventDefault();
    setEmailState('sending');
    setError(null);
    try {
      await apiFetch('/v1/auth/magic-link', { method: 'POST', body: JSON.stringify({ email }) });
      setEmailState('sent');
    } catch (err) {
      setEmailState('idle');
      setError(ERROR_COPY[errCode(err)] ?? 'Could not send the link.');
    }
  }

  async function requestSmsCode(e: React.FormEvent) {
    e.preventDefault();
    setSmsBusy(true);
    setError(null);
    const full = toE164(dialFor(country), phoneLocal);
    try {
      await apiFetch('/v1/auth/sms/request', {
        method: 'POST',
        body: JSON.stringify({ phone: full }),
      });
      setSentTo(full);
      setSmsStep('code');
    } catch (err) {
      setError(ERROR_COPY[errCode(err)] ?? 'Could not send the SMS.');
    } finally {
      setSmsBusy(false);
    }
  }

  async function verifySmsCode(e: React.FormEvent) {
    e.preventDefault();
    setSmsBusy(true);
    setError(null);
    try {
      await apiFetch('/v1/auth/sms/verify', {
        method: 'POST',
        body: JSON.stringify({ phone: sentTo, code }),
      });
      window.location.href = '/dashboard';
    } catch (err) {
      setError(ERROR_COPY[errCode(err)] ?? 'Could not verify the code.');
      setSmsBusy(false);
    }
  }

  const hasOAuth = providers.google || providers.github;

  return (
    <div className="flex min-h-screen items-center justify-center px-6">
      <div className="w-full max-w-sm">
        <Logo className="mb-10" />
        <h1 className="text-[20px] font-semibold tracking-tight">Sign in to your workspace</h1>
        <p className="mt-1 text-[13px] text-[--color-fg-muted]">
          Passwordless — pick whichever is easiest.
        </p>

        {/* Providers still loading → neutral skeleton instead of a blank card
            (previously nothing rendered until the fetch resolved). */}
        {providersState === 'loading' && (
          <div aria-hidden className="mt-7 space-y-2">
            <div className="h-10 w-full animate-pulse rounded-md bg-[--color-bg-elevated]" />
            <div className="h-10 w-full animate-pulse rounded-md bg-[--color-bg-elevated]" />
          </div>
        )}

        {hasOAuth && (
          <div className="mt-7 space-y-2">
            {providers.google && (
              <a
                href={apiUrl('/v1/auth/google')}
                className="flex h-10 w-full items-center justify-center gap-2.5 rounded-md border border-[--color-border] bg-[--color-bg-elevated] text-[13px] font-medium text-[--color-fg] transition-colors duration-200 hover:border-[--color-border-strong]"
              >
                <GoogleIcon />
                Continue with Google
              </a>
            )}
            {providers.github && (
              <a
                href={apiUrl('/v1/auth/github')}
                className="flex h-10 w-full items-center justify-center gap-2.5 rounded-md border border-[--color-border] bg-[--color-bg-elevated] text-[13px] font-medium text-[--color-fg] transition-colors duration-200 hover:border-[--color-border-strong]"
              >
                <GitHubIcon />
                Continue with GitHub
              </a>
            )}
          </div>
        )}

        {hasOAuth && (providers.email || providers.sms) && (
          <div className="my-5 flex items-center gap-3">
            <span className="h-px flex-1 bg-[--color-border]" />
            <span className="text-[11px] uppercase tracking-wider text-[--color-fg-subtle]">
              or
            </span>
            <span className="h-px flex-1 bg-[--color-border]" />
          </div>
        )}

        <div className={providers.sms || providers.email ? 'mt-4' : hasOAuth ? '' : 'mt-7'}>
          {providers.email && !phoneOpen && emailState !== 'sent' && (
            <form onSubmit={sendMagicLink} className="space-y-3">
              <div className="space-y-1.5">
                <Label htmlFor="email">Email</Label>
                <Input
                  id="email"
                  type="email"
                  required
                  autoComplete="email"
                  value={email}
                  onChange={(e) => setEmail(e.target.value)}
                  placeholder="you@company.com"
                />
              </div>
              <Button
                type="submit"
                variant="primary"
                size="lg"
                className="w-full"
                disabled={emailState === 'sending'}
              >
                {emailState === 'sending' ? 'Sending…' : 'Send magic link'}
              </Button>
            </form>
          )}

          {providers.email && !phoneOpen && emailState === 'sent' && (
            <div className="panel p-4">
              <p className="text-[13px]">
                Magic link sent to <span className="mono">{email}</span>.
              </p>
              <p className="mt-1.5 text-[12px] text-[--color-fg-muted]">
                Open it on this device to finish signing in.
              </p>
            </div>
          )}

          {providers.sms && phoneOpen && smsStep === 'phone' && (
            <form onSubmit={requestSmsCode} className="space-y-3">
              <div className="space-y-1.5">
                <Label htmlFor="country">Country</Label>
                <CountryPicker countries={COUNTRIES} value={country} onChange={setCountry} />
              </div>
              <div className="space-y-1.5">
                <Label htmlFor="phone" hint={dialFor(country)}>
                  Phone number
                </Label>
                <Input
                  id="phone"
                  type="tel"
                  inputMode="tel"
                  required
                  autoComplete="tel-national"
                  value={phoneLocal}
                  onChange={(e) => setPhoneLocal(e.target.value)}
                  placeholder="79 123 45 67"
                />
              </div>
              <Button
                type="submit"
                variant="primary"
                size="lg"
                className="w-full"
                disabled={smsBusy}
              >
                {smsBusy ? 'Sending…' : 'Send code'}
              </Button>
            </form>
          )}

          {providers.sms && phoneOpen && smsStep === 'code' && (
            <form onSubmit={verifySmsCode} className="space-y-3">
              <div className="space-y-1.5">
                <Label htmlFor="code" hint={`sent to ${sentTo}`}>
                  6-digit code
                </Label>
                <Input
                  id="code"
                  inputMode="numeric"
                  autoComplete="one-time-code"
                  required
                  maxLength={6}
                  value={code}
                  onChange={(e) => setCode(e.target.value.replace(/\D/g, ''))}
                  placeholder="123456"
                  className="mono tracking-[0.3em]"
                />
              </div>
              <Button
                type="submit"
                variant="primary"
                size="lg"
                className="w-full"
                disabled={smsBusy || code.length !== 6}
              >
                {smsBusy ? 'Verifying…' : 'Verify & sign in'}
              </Button>
              <button
                type="button"
                onClick={() => {
                  setSmsStep('phone');
                  setCode('');
                  setError(null);
                }}
                className="w-full text-[12px] text-[--color-fg-muted] transition-colors hover:text-[--color-fg]"
              >
                ← Use a different number
              </button>
            </form>
          )}

          {error && <p className="mt-3 text-[12px] text-[--color-danger]">{error}</p>}

          {/* Phone sign-in stays available but demoted: expand link when any
              other provider exists, collapse link to get back. */}
          {providers.sms && !phoneOpen && (hasOAuth || providers.email) && (
            <button
              type="button"
              onClick={() => {
                setPhoneOpen(true);
                setError(null);
              }}
              className="mt-4 w-full text-center text-[12px] text-[--color-fg-muted] underline-offset-2 transition-colors hover:text-[--color-fg] hover:underline"
            >
              Sign in with phone number instead
            </button>
          )}
          {providers.sms && phoneOpen && (hasOAuth || providers.email) && (
            <button
              type="button"
              onClick={() => {
                setPhoneOpen(false);
                setSmsStep('phone');
                setCode('');
                setError(null);
              }}
              className="mt-4 w-full text-center text-[12px] text-[--color-fg-muted] underline-offset-2 transition-colors hover:text-[--color-fg] hover:underline"
            >
              ← Use another sign-in method
            </button>
          )}
        </div>

        <div className="mt-8 text-[12px] text-[--color-fg-subtle]">
          <Link href="/" className="transition-colors hover:text-[--color-fg]">
            ← Back to home
          </Link>
        </div>
      </div>
    </div>
  );
}

function GoogleIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 18 18" aria-hidden="true">
      <path
        fill="#4285F4"
        d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844a4.14 4.14 0 0 1-1.796 2.716v2.259h2.908c1.702-1.567 2.684-3.875 2.684-6.615Z"
      />
      <path
        fill="#34A853"
        d="M9 18c2.43 0 4.467-.806 5.956-2.184l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 0 0 9 18Z"
      />
      <path
        fill="#FBBC05"
        d="M3.964 10.706A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.706V4.962H.957A8.997 8.997 0 0 0 0 9c0 1.452.348 2.827.957 4.038l3.007-2.332Z"
      />
      <path
        fill="#EA4335"
        d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.962L3.964 7.294C4.672 5.167 6.656 3.58 9 3.58Z"
      />
    </svg>
  );
}

function GitHubIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
      <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82a7.6 7.6 0 0 1 2-.27c.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" />
    </svg>
  );
}
