'use client'

import { useState, type FormEvent } from 'react'
import { Eye, EyeOff, AlertTriangle, Loader2 } from 'lucide-react'
import { ActuateBrandLogo } from '../assets/actuate-logo.js'

export interface CaptchaConfig {
  provider: 'recaptcha' | 'turnstile' | 'none'
  siteKey: string | null
}

/**
 * Branding shown on the login screen. Mirrors `config.admin.branding` from the
 * sidebar so integrators only need to set it once. `logo` should be a URL or
 * data URL; pass `null` to opt out of any logo entirely.
 */
export interface LoginBrandingConfig {
  logo?: string | null
  name?: string
  tagline?: string
}

export interface LoginProps {
  onLogin: (
    email: string,
    password: string,
    captchaToken?: string,
  ) => Promise<{
    success: boolean
    error?: string
    /** Set when the account has TOTP enabled — the password step succeeded but
     * a second factor is still required. */
    requiresTOTP?: boolean
    /** Short-lived token tying the second-factor step to this password step. */
    mfaPendingToken?: string
  }>
  /**
   * Completes the second-factor step. Receives the `mfaPendingToken` from
   * `onLogin` plus the user's authenticator code (or a one-time backup code).
   * Required for accounts with MFA enabled; when omitted, the challenge screen
   * explains that MFA sign-in isn't wired up.
   */
  onVerifyTotp?: (
    mfaPendingToken: string,
    code: string,
  ) => Promise<{ success: boolean; error?: string }>
  onNavigate?: (path: string) => void
  oauthProviders?: string[]
  captchaConfig?: CaptchaConfig
  /**
   * Custom branding for the login screen. Falls back to the bundled Actuate
   * Media wordmark + "Actuate CMS" headline when omitted.
   */
  branding?: LoginBrandingConfig
}

const OAUTH_LABELS: Record<string, string> = {
  google: 'Google',
  github: 'GitHub',
  microsoft: 'Microsoft',
}

const OAUTH_COLORS: Record<string, { border: string; bg: string; text: string; hover: string }> = {
  google: {
    border: 'border-gray-300',
    bg: 'bg-white',
    text: 'text-gray-700',
    hover: 'hover:bg-gray-50',
  },
  github: {
    border: 'border-gray-800',
    bg: 'bg-gray-900',
    text: 'text-white',
    hover: 'hover:bg-gray-800',
  },
  microsoft: {
    border: 'border-blue-500',
    bg: 'bg-white',
    text: 'text-gray-700',
    hover: 'hover:bg-blue-50',
  },
}

function GoogleIcon() {
  return (
    <svg className="h-5 w-5" viewBox="0 0 24 24" aria-hidden="true">
      <path
        d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z"
        fill="#4285F4"
      />
      <path
        d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
        fill="#34A853"
      />
      <path
        d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
        fill="#FBBC05"
      />
      <path
        d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
        fill="#EA4335"
      />
    </svg>
  )
}

function MicrosoftIcon() {
  return (
    <svg className="h-5 w-5" viewBox="0 0 21 21" aria-hidden="true">
      <rect x="1" y="1" width="9" height="9" fill="#F25022" />
      <rect x="1" y="11" width="9" height="9" fill="#00A4EF" />
      <rect x="11" y="1" width="9" height="9" fill="#7FBA00" />
      <rect x="11" y="11" width="9" height="9" fill="#FFB900" />
    </svg>
  )
}

function OAuthIcon({ provider }: { provider: string }) {
  switch (provider) {
    case 'google':
      return <GoogleIcon />
    case 'github':
      return (
        <svg className="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
          <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
        </svg>
      )
    case 'microsoft':
      return <MicrosoftIcon />
    default:
      return null
  }
}

function useCaptchaScript(provider: string | undefined, siteKey: string | null | undefined) {
  const [loaded, setLoaded] = useState(false)

  useState(() => {
    if (typeof window === 'undefined' || !siteKey) return

    if (provider === 'recaptcha') {
      const existing = document.querySelector('script[src*="recaptcha"]')
      if (existing) {
        setLoaded(true)
        return
      }
      const script = document.createElement('script')
      script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`
      script.async = true
      script.onload = () => setLoaded(true)
      document.head.appendChild(script)
    } else if (provider === 'turnstile') {
      const existing = document.querySelector('script[src*="turnstile"]')
      if (existing) {
        setLoaded(true)
        return
      }
      const script = document.createElement('script')
      script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js'
      script.async = true
      script.onload = () => setLoaded(true)
      document.head.appendChild(script)
    }
  })

  return loaded
}

async function getCaptchaToken(provider: string, siteKey: string, action: string): Promise<string> {
  if (provider === 'recaptcha') {
    const grecaptcha = (window as any).grecaptcha
    if (!grecaptcha) return ''
    return new Promise((resolve) => {
      grecaptcha.ready(() => {
        grecaptcha
          .execute(siteKey, { action })
          .then(resolve)
          .catch(() => resolve(''))
      })
    })
  }

  if (provider === 'turnstile') {
    const turnstile = (window as any).turnstile
    if (!turnstile) return ''
    return new Promise((resolve) => {
      const container = document.createElement('div')
      container.style.display = 'none'
      document.body.appendChild(container)
      turnstile.render(container, {
        sitekey: siteKey,
        action,
        callback: (token: string) => {
          container.remove()
          resolve(token)
        },
        'error-callback': () => {
          container.remove()
          resolve('')
        },
      })
    })
  }

  return ''
}

export function Login({
  onLogin,
  onVerifyTotp,
  onNavigate,
  oauthProviders,
  captchaConfig,
  branding,
}: LoginProps) {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [showPassword, setShowPassword] = useState(false)
  const [submitting, setSubmitting] = useState(false)
  const [error, setError] = useState('')

  // Second-factor challenge state. When `mfaPendingToken` is set we swap the
  // email/password form for the TOTP code entry.
  const [mfaPendingToken, setMfaPendingToken] = useState<string | null>(null)
  const [totpCode, setTotpCode] = useState('')
  const [useBackupCode, setUseBackupCode] = useState(false)
  const [totpError, setTotpError] = useState('')
  const [verifying, setVerifying] = useState(false)

  useCaptchaScript(captchaConfig?.provider, captchaConfig?.siteKey)

  const canSubmit = email.trim() && password && !submitting

  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault()
    if (!canSubmit) return

    setError('')
    setSubmitting(true)

    try {
      let captchaToken: string | undefined
      if (captchaConfig?.provider && captchaConfig.provider !== 'none' && captchaConfig.siteKey) {
        captchaToken = await getCaptchaToken(captchaConfig.provider, captchaConfig.siteKey, 'login')
        if (!captchaToken) {
          setError('CAPTCHA verification failed. Please try again.')
          setSubmitting(false)
          return
        }
      }

      const result = await onLogin(email.trim(), password, captchaToken)

      if (result.requiresTOTP && result.mfaPendingToken) {
        // Password verified — advance to the second-factor step.
        setMfaPendingToken(result.mfaPendingToken)
        setPassword('')
        return
      }

      if (!result.success) {
        setError(result.error ?? 'Invalid email or password')
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : 'An unexpected error occurred')
    } finally {
      setSubmitting(false)
    }
  }

  const handleVerifyTotp = async (e: FormEvent) => {
    e.preventDefault()
    const code = totpCode.trim()
    if (!code || !mfaPendingToken) return

    setTotpError('')

    if (!onVerifyTotp) {
      setTotpError('Two-factor sign-in is not available. Contact your administrator.')
      return
    }

    setVerifying(true)
    try {
      const result = await onVerifyTotp(mfaPendingToken, code)
      if (!result.success) {
        setTotpError(result.error ?? 'That code was not accepted. Try again.')
      }
    } catch (err) {
      setTotpError(err instanceof Error ? err.message : 'An unexpected error occurred')
    } finally {
      setVerifying(false)
    }
  }

  const resetToPasswordStep = () => {
    setMfaPendingToken(null)
    setTotpCode('')
    setUseBackupCode(false)
    setTotpError('')
    setError('')
  }

  const handleOAuthClick = (provider: string) => {
    window.location.href = `/api/cms/auth/oauth/${provider}`
  }

  const enabledProviders =
    oauthProviders?.filter((p) => ['google', 'github', 'microsoft'].includes(p)) ?? []

  // Branding fallbacks: when an integrator doesn't pass `branding`, render the
  // Actuate Media wordmark inline (transparent SVG, no surrounding pill needed).
  // When `branding.logo` is explicitly `null`, render no logo at all (just the
  // headline + tagline) so whitelabel deployments can hide it entirely.
  const customLogo = branding?.logo
  const brandName = branding?.name ?? 'Actuate CMS'
  const brandTagline = branding?.tagline ?? 'Sign in to your account'
  const showLogo = customLogo !== null

  return (
    <div className="flex min-h-screen items-center justify-center bg-gray-50 px-4">
      <div className="w-full max-w-md">
        <div className="mb-8 text-center">
          {showLogo &&
            (customLogo ? (
              <img
                src={customLogo}
                alt={brandName}
                className="mx-auto mb-4 max-h-16 w-auto object-contain"
                draggable={false}
              />
            ) : (
              <ActuateBrandLogo className="mx-auto mb-4 h-14 w-auto" />
            ))}
          <h1 className="text-2xl font-bold text-gray-900">{brandName}</h1>
          <p className="mt-2 text-gray-600">{brandTagline}</p>
        </div>

        {mfaPendingToken ? (
          <form
            onSubmit={handleVerifyTotp}
            className="space-y-5 rounded-xl border border-gray-200 bg-white p-6 shadow-sm"
          >
            <div>
              <h2 className="text-lg font-semibold text-gray-900">Two-factor authentication</h2>
              <p className="mt-1 text-sm text-gray-600">
                {useBackupCode
                  ? 'Enter one of your one-time backup codes.'
                  : 'Enter the 6-digit code from your authenticator app.'}
              </p>
            </div>

            {totpError && (
              <div className="flex items-start gap-3 rounded-lg border border-red-200 bg-red-50 p-3">
                <AlertTriangle className="mt-0.5 h-5 w-5 shrink-0 text-red-600" />
                <p className="text-sm text-red-800">{totpError}</p>
              </div>
            )}

            <div>
              <label htmlFor="totp-code" className="mb-1.5 block text-sm font-medium text-gray-700">
                {useBackupCode ? 'Backup code' : 'Authentication code'}
              </label>
              <input
                id="totp-code"
                type="text"
                inputMode={useBackupCode ? 'text' : 'numeric'}
                autoComplete="one-time-code"
                value={totpCode}
                onChange={(e) =>
                  setTotpCode(
                    useBackupCode
                      ? e.target.value.trim()
                      : e.target.value.replace(/\D/g, '').slice(0, 6),
                  )
                }
                placeholder={useBackupCode ? 'abcd1234' : '123456'}
                className="w-full rounded-lg border border-gray-300 px-3 py-2.5 text-center font-mono text-lg tracking-[0.3em] focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none"
                autoFocus
                required
              />
            </div>

            <button
              type="submit"
              disabled={!totpCode.trim() || verifying}
              className="flex w-full items-center justify-center gap-2 rounded-lg bg-blue-600 py-2.5 font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50"
            >
              {verifying ? (
                <>
                  <Loader2 className="h-4 w-4 animate-spin" />
                  Verifying...
                </>
              ) : (
                'Verify'
              )}
            </button>

            <div className="flex items-center justify-between text-xs">
              <button
                type="button"
                onClick={() => {
                  setUseBackupCode((v) => !v)
                  setTotpCode('')
                  setTotpError('')
                }}
                className="font-medium text-blue-600 hover:text-blue-700"
              >
                {useBackupCode ? 'Use authenticator app instead' : 'Use a backup code'}
              </button>
              <button
                type="button"
                onClick={resetToPasswordStep}
                className="font-medium text-gray-500 hover:text-gray-700"
              >
                Back to sign in
              </button>
            </div>
          </form>
        ) : (
          <form
            onSubmit={handleSubmit}
            className="space-y-5 rounded-xl border border-gray-200 bg-white p-6 shadow-sm"
          >
            {error && (
              <div className="flex items-start gap-3 rounded-lg border border-red-200 bg-red-50 p-3">
                <AlertTriangle className="mt-0.5 h-5 w-5 shrink-0 text-red-600" />
                <p className="text-sm text-red-800">{error}</p>
              </div>
            )}

            <div>
              <label
                htmlFor="login-email"
                className="mb-1.5 block text-sm font-medium text-gray-700"
              >
                Email Address
              </label>
              <input
                id="login-email"
                type="email"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                placeholder="admin@example.com"
                className="w-full rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none"
                required
                autoFocus
                autoComplete="email"
              />
            </div>

            <div>
              <div className="mb-1.5 flex items-center justify-between">
                <label htmlFor="login-password" className="block text-sm font-medium text-gray-700">
                  Password
                </label>
                {onNavigate && (
                  <button
                    type="button"
                    onClick={() => onNavigate('/forgot-password')}
                    className="text-xs font-medium text-blue-600 hover:text-blue-700"
                  >
                    Forgot Password?
                  </button>
                )}
              </div>
              <div className="relative">
                <input
                  id="login-password"
                  type={showPassword ? 'text' : 'password'}
                  value={password}
                  onChange={(e) => setPassword(e.target.value)}
                  placeholder="Enter your password"
                  className="w-full rounded-lg border border-gray-300 px-3 py-2.5 pr-10 text-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none"
                  required
                  autoComplete="current-password"
                />
                <button
                  type="button"
                  onClick={() => setShowPassword(!showPassword)}
                  className="absolute top-1/2 right-3 -translate-y-1/2 text-gray-400 hover:text-gray-600"
                  tabIndex={-1}
                >
                  {showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
                </button>
              </div>
            </div>

            <button
              type="submit"
              disabled={!canSubmit}
              className="flex w-full items-center justify-center gap-2 rounded-lg bg-blue-600 py-2.5 font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50"
            >
              {submitting ? (
                <>
                  <Loader2 className="h-4 w-4 animate-spin" />
                  Signing in...
                </>
              ) : (
                'Sign In'
              )}
            </button>

            {enabledProviders.length > 0 && (
              <>
                <div className="relative">
                  <div className="absolute inset-0 flex items-center">
                    <div className="w-full border-t border-gray-200" />
                  </div>
                  <div className="relative flex justify-center text-sm">
                    <span className="bg-white px-3 text-gray-500">Or continue with</span>
                  </div>
                </div>

                <div className="grid gap-3">
                  {enabledProviders.map((provider) => {
                    const colors = OAUTH_COLORS[provider] ?? OAUTH_COLORS.google
                    return (
                      <button
                        key={provider}
                        type="button"
                        onClick={() => handleOAuthClick(provider)}
                        className={`flex w-full items-center justify-center gap-3 border px-4 py-2.5 ${colors?.border ?? ''} ${colors?.bg ?? ''} ${colors?.text ?? ''} rounded-lg text-sm font-medium ${colors?.hover ?? ''} transition-colors`}
                      >
                        <OAuthIcon provider={provider} />
                        {OAUTH_LABELS[provider] ?? provider}
                      </button>
                    )
                  })}
                </div>
              </>
            )}
          </form>
        )}
      </div>
    </div>
  )
}
