import { useEffect, useState } from 'react'
import { Link, useNavigate, useLocation } from 'react-router-dom'
import MoonDot from '../../components/MoonDot.jsx'
import BrandLogo from '../../components/BrandLogo.jsx'
import ProviderIcon from '../../components/ProviderIcon.jsx'
import TrustLimitModal from '../../components/security/TrustLimitModal.jsx'
import { useAuth } from '../../contexts/AuthContext.jsx'
import { useSite } from '../../contexts/SiteContext.jsx'
import { api } from '../../api/client.js'

// Friendly copy for the ?sso_error codes the SSO callback can redirect back with.
const SSO_ERRORS = {
  not_linked: 'That account is not linked to an admin user. Sign in with your password, then link it under Account.',
  disabled: 'This account is not active. Contact an administrator.',
  denied: 'Sign-in was cancelled.',
  unavailable: 'That sign-in method is not available right now.',
  bad_state: 'Your sign-in session expired. Please try again.',
  error: 'Could not complete sign-in. Please try again.',
}

// Hidden anti-bot field. Off-screen via CSS (NOT display:none/hidden, which bots
// skip) so real users never fill it but naive scripted bots do. Name must match
// the server's HONEYPOT_FIELD ('company').
const honeypotStyle = {
  position: 'absolute',
  left: '-9999px',
  top: 'auto',
  width: '1px',
  height: '1px',
  opacity: 0,
  pointerEvents: 'none',
}

export default function AdminLogin() {
  const { user, login, loginTotp, ssoLoginTotp } = useAuth()
  const { siteTitle, heroImage } = useSite()
  const BG = `linear-gradient(180deg,rgba(11,15,20,0.72),rgba(11,15,20,0.9)),url('${heroImage}')`
  const navigate = useNavigate()
  const location = useLocation()
  const dest = location.state?.from?.pathname || '/admin'
  // A player who signs in here belongs in the player portal, not the admin shell
  // (the admin API 403s them anyway). Staff go to their intended admin dest.
  const destFor = (u) => (u && u.role === 'player' ? '/account' : dest)

  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [company, setCompany] = useState('') // honeypot — must stay empty
  const [error, setError] = useState('')
  const [busy, setBusy] = useState(false)

  // Two-factor step state. `ssoTotp` marks the SSO variant: the challenge lives in
  // an httpOnly cookie (not React state), so the code posts to a different endpoint.
  const [stage, setStage] = useState('creds') // 'creds' | 'totp'
  const [challenge, setChallenge] = useState('')
  const [code, setCode] = useState('')
  const [ssoTotp, setSsoTotp] = useState(false)
  const [trustDevice, setTrustDevice] = useState(false)
  const [useRecovery, setUseRecovery] = useState(false)
  const [trustLimit, setTrustLimit] = useState(null) // { devices, dest } when the cap is hit

  // SSO providers to offer (empty if none configured) + any error the callback
  // bounced us back with (?sso_error=...).
  const [providers, setProviders] = useState([])
  const ssoError = SSO_ERRORS[new URLSearchParams(location.search).get('sso_error')] || ''

  // Already signed in → go straight to the right home for the role.
  useEffect(() => {
    if (user) navigate(destFor(user), { replace: true })
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [user, dest, navigate])

  // The SSO callback bounces 2FA accounts back here with ?sso_totp=1 after the IdP
  // step: it has staged an httpOnly TOTP challenge and needs the authenticator code
  // before it will issue a session. Jump straight to the code step.
  useEffect(() => {
    if (new URLSearchParams(location.search).get('sso_totp')) {
      setStage('totp')
      setSsoTotp(true)
    }
  }, [location.search])

  // Load enabled SSO providers for the buttons. Failure is non-fatal — the page
  // still works with password login and simply shows no provider buttons.
  useEffect(() => {
    let active = true
    api
      .authProviders()
      .then((list) => active && setProviders(Array.isArray(list) ? list : []))
      .catch(() => active && setProviders([]))
    return () => {
      active = false
    }
  }, [])

  // Full-page redirect into the provider's OAuth flow, preserving the intended
  // destination so the callback can return the user there.
  function startSso(provider) {
    const q = dest && dest !== '/admin' ? `?returnTo=${encodeURIComponent(dest)}` : ''
    window.location.assign(provider.loginUrl + q)
  }

  async function onSubmit(e) {
    e.preventDefault()
    setError('')
    setBusy(true)
    try {
      const data = await login(username, password, { company })
      if (data.totpRequired) {
        setChallenge(data.challenge)
        setStage('totp')
        setBusy(false)
        return
      }
      navigate(destFor(data.user), { replace: true })
    } catch (err) {
      setError(err.status === 401 ? 'Incorrect username or password.' : 'Could not sign in right now.')
      setBusy(false)
    }
  }

  async function onSubmitTotp(e) {
    e.preventDefault()
    setError('')
    setBusy(true)
    try {
      if (ssoTotp) {
        // Trust works on the SSO second factor exactly as it does on the password
        // one — the IdP already proved the first factor.
        const data = await ssoLoginTotp(code.trim(), { trustDevice })
        const to = data.returnTo || '/admin'
        if (data.trustLimitReached) {
          setTrustLimit({ devices: data.devices || [], dest: to })
          setBusy(false)
          return
        }
        navigate(to, { replace: true })
      } else {
        const entered = code.trim()
        const data = await loginTotp(challenge, useRecovery ? '' : entered, {
          recoveryCode: useRecovery ? entered : undefined,
          trustDevice,
        })
        const to = destFor(data.user)
        if (data.trustLimitReached) {
          setTrustLimit({ devices: data.devices || [], dest: to })
          setBusy(false)
          return
        }
        navigate(to, { replace: true })
      }
    } catch (err) {
      const expired = err.status === 401 && /expired/i.test(err.message)
      const badRecovery = useRecovery ? 'That recovery code is not valid.' : 'Invalid verification code.'
      setError(expired ? 'Your verification session expired. Please sign in again.' : badRecovery)
      setBusy(false)
      if (expired) {
        setStage('creds')
        setSsoTotp(false)
      }
    }
  }

  let submitLabel = 'Sign in'
  if (busy) submitLabel = 'Signing in…'
  else if (stage === 'totp') submitLabel = 'Verify'

  return (
    <main
      style={{
        minHeight: '100vh',
        display: 'grid',
        placeItems: 'center',
        padding: '40px 18px',
        overflow: 'hidden',
        backgroundColor: 'var(--bg-deep)',
        backgroundImage: BG,
        backgroundPosition: 'center',
        backgroundRepeat: 'no-repeat',
        backgroundSize: 'min(60vh, 520px)',
      }}
    >
      <div style={{ width: '100%', maxWidth: 400 }}>
        <div style={{ textAlign: 'center', marginBottom: 26 }}>
          <div style={{ marginBottom: 14 }}>
            {/* Stacked above the moon rather than beside it: this layout is
                centered text, and a flex row here would change the block's
                height on instances with no logo. */}
            <BrandLogo height={34} style={{ margin: '0 auto 12px' }} />
            <MoonDot size={15} glow={0.55} />
          </div>
          <h1 className="display" style={{ margin: 0, fontSize: '1.7rem', letterSpacing: '0.04em', color: 'var(--head)' }}>
            {siteTitle}
          </h1>
          <p className="sans" style={{ margin: '6px 0 0', color: '#9aa6b4', fontSize: '0.8rem', letterSpacing: '0.16em', textTransform: 'uppercase' }}>
            Admin Panel
          </p>
        </div>

        <form
          onSubmit={stage === 'totp' ? onSubmitTotp : onSubmit}
          style={{
            border: '1px solid var(--line)',
            borderRadius: 12,
            padding: 28,
            background: 'linear-gradient(180deg,rgba(25,34,49,0.92),rgba(20,26,33,0.92))',
            backdropFilter: 'blur(6px)',
            boxShadow: '0 24px 60px rgba(0,0,0,0.5)',
          }}
        >
          {stage === 'creds' ? (
            <>
              <label style={{ display: 'block', marginBottom: 16 }}>
                <span className="field-label">Username</span>
                <input
                  type="text"
                  autoComplete="username"
                  autoFocus
                  value={username}
                  onChange={(e) => setUsername(e.target.value)}
                  className="input"
                />
              </label>
              <label style={{ display: 'block', marginBottom: 22 }}>
                <span className="field-label">Password</span>
                <input
                  type="password"
                  autoComplete="current-password"
                  value={password}
                  onChange={(e) => setPassword(e.target.value)}
                  className="input"
                />
              </label>

              {/* Honeypot: hidden from humans, left empty; bots that fill it are rejected. */}
              <div style={honeypotStyle} aria-hidden="true">
                <label>
                  Company
                  <input
                    type="text"
                    name="company"
                    tabIndex={-1}
                    autoComplete="off"
                    value={company}
                    onChange={(e) => setCompany(e.target.value)}
                  />
                </label>
              </div>
            </>
          ) : (
            <>
              <label style={{ display: 'block', marginBottom: 14 }}>
                <span className="field-label">{useRecovery ? 'Recovery code' : 'Authentication code'}</span>
                <input
                  type="text"
                  inputMode={useRecovery ? 'text' : 'numeric'}
                  autoComplete="one-time-code"
                  autoFocus
                  placeholder={useRecovery ? 'xxxxx-xxxxx' : '6-digit code'}
                  value={code}
                  onChange={(e) => setCode(e.target.value)}
                  className="input"
                />
                <span className="sans" style={{ display: 'block', marginTop: 8, color: 'var(--dim)', fontSize: '0.76rem' }}>
                  {useRecovery ? 'Enter one of your saved single-use recovery codes.' : 'Enter the code from your authenticator app.'}
                </span>
              </label>
              {/* Offered on the SSO second factor too — the trust is on the device,
                  not on how the first factor was proved. */}
              <label className="sans" style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12, color: 'var(--muted)', fontSize: '0.84rem' }}>
                <input type="checkbox" checked={trustDevice} onChange={(e) => setTrustDevice(e.target.checked)} />
                Trust this device for 30 days (skip the code next time)
              </label>
              {/* Recovery codes remain password-login only: the SSO second step
                  verifies an authenticator code against the staged challenge. */}
              {!ssoTotp && (
                <button
                  type="button"
                  onClick={() => { setUseRecovery((v) => !v); setCode('') }}
                  className="sans"
                  style={{ display: 'block', marginBottom: 22, background: 'none', border: 'none', padding: 0, color: 'var(--accent)', cursor: 'pointer', fontSize: '0.8rem' }}
                >
                  {useRecovery ? 'Use an authenticator code instead' : 'Use a recovery code instead'}
                </button>
              )}
            </>
          )}

          {(error || (stage === 'creds' && ssoError)) && (
            <p className="sans" style={{ margin: '0 0 14px', color: '#d98b84', fontSize: '0.85rem', textAlign: 'center', lineHeight: 1.5 }}>
              {error || ssoError}
            </p>
          )}

          <button
            type="submit"
            disabled={busy}
            className="btn btn-primary"
            style={{ display: 'block', width: '100%', borderRadius: 8, padding: 12, textAlign: 'center' }}
          >
            {submitLabel}
          </button>

          {/* SSO providers — only on the credentials step, only if any are enabled. */}
          {stage === 'creds' && providers.length > 0 && (
            <div style={{ marginTop: 20 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 12, margin: '0 0 16px', color: 'var(--dim)' }}>
                <span style={{ flex: 1, height: 1, background: 'var(--line)' }} />
                <span className="sans" style={{ fontSize: '0.72rem', letterSpacing: '0.14em', textTransform: 'uppercase' }}>or</span>
                <span style={{ flex: 1, height: 1, background: 'var(--line)' }} />
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                {providers.map((p) => (
                  <button
                    key={p.id}
                    type="button"
                    onClick={() => startSso(p)}
                    className="btn"
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      justifyContent: 'center',
                      gap: 10,
                      width: '100%',
                      borderRadius: 8,
                      padding: 11,
                      border: '1px solid var(--line)',
                      background: 'rgba(255,255,255,0.04)',
                      color: 'var(--ink)',
                    }}
                  >
                    <span style={{ display: 'inline-flex', width: 18, height: 18 }}>
                      <ProviderIcon icon={p.icon} size={18} />
                    </span>
                    Continue with {p.name}
                  </button>
                ))}
              </div>
            </div>
          )}

          <p className="sans" style={{ margin: '16px 0 0', textAlign: 'center', color: 'var(--dim)', fontSize: '0.76rem' }}>
            Protected area — not indexed. Sessions expire after 1 day.
          </p>
        </form>
        <p style={{ textAlign: 'center', margin: '20px 0 0' }}>
          <Link to="/" className="sans" style={{ color: 'var(--accent)', fontSize: '0.84rem', textDecoration: 'none' }}>
            ← Back to site
          </Link>
        </p>
      </div>

      {trustLimit && (
        <TrustLimitModal
          devices={trustLimit.devices}
          onTrusted={() => navigate(trustLimit.dest, { replace: true })}
          onCancel={() => navigate(trustLimit.dest, { replace: true })}
        />
      )}
    </main>
  )
}
