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

// Friendly copy for the ?sso_error codes the SSO callback can bounce back with.
const SSO_ERRORS = {
  not_linked:
    'That account is not linked to a player. Enable SSO sign-up, or sign in with a password and link it under your 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.',
}

export default function PlayerLogin() {
  const { user, login, loginTotp, ssoLoginTotp } = useAuth()
  const navigate = useNavigate()
  const location = useLocation()
  const dest = location.state?.from?.pathname || '/player'
  // A staff member who signs in here belongs in the admin shell, not the portal.
  const destFor = (u) => (u && u.role !== 'player' ? '/admin' : 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)

  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)
  // When trust was requested at login but the device cap is reached: show the
  // revoke-to-continue modal, then navigate on resolve. `pendingDest` holds where
  // to go once the prompt is dealt with.
  const [trustLimit, setTrustLimit] = useState(null) // { devices, dest }

  const [providers, setProviders] = useState([])
  const [canRegister, setCanRegister] = useState(false)
  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.
  useEffect(() => {
    if (new URLSearchParams(location.search).get('sso_totp')) {
      setStage('totp')
      setSsoTotp(true)
    }
  }, [location.search])

  // SSO providers (for buttons) + whether password registration is open.
  useEffect(() => {
    let active = true
    api
      .authProviders()
      .then((list) => active && setProviders(Array.isArray(list) ? list : []))
      .catch(() => active && setProviders([]))
    api
      .publicSettings()
      .then((s) => active && setCanRegister(Boolean(s?.registration?.password)))
      .catch(() => {})
    return () => {
      active = false
    }
  }, [])

  function startSso(provider) {
    // Always return into the player portal so the callback lands on /account*.
    const q = `?returnTo=${encodeURIComponent(dest.startsWith('/account') ? dest : '/account')}`
    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) {
      if (err.status === 403) setError('This account is not active. Contact an administrator.')
      else 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 too. On the mobile bridge this page
        // is running inside the app's Custom Tab, so the cookie set here is what
        // lets the next app sign-in skip the code.
        const data = await ssoLoginTotp(code.trim(), { trustDevice })
        // Native SSO bridge (M9): a mobile 2FA completion returns an absolute
        // deep link (e.g. runicgateway://…) to hand the app its one-time code.
        // React Router can't navigate a custom scheme, so leave the SPA for it.
        // This wins over the trust-cap prompt: the sign-in itself succeeded and the
        // deep link is single-use, so stalling here to manage devices would strand
        // the app. An over-cap user simply isn't trusted and can prune the list
        // from Account → Trusted Devices.
        if (data.redirect) {
          window.location.href = data.redirect
          return
        }
        const to = data.returnTo || '/account'
        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)
        // Trust was requested but the device cap is reached: the session is already
        // issued, so prompt to revoke one before trusting, then navigate.
        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 (
    <PlayerShell
      subtitle="Player sign-in"
      footer={
        <div style={{ margin: '16px 0 0', textAlign: 'center' }}>
          <p className="sans" style={{ margin: 0, color: 'var(--dim)', fontSize: '0.84rem' }}>
            <Link to="/account/forgot" style={{ color: 'var(--accent)', textDecoration: 'none' }}>
              Forgot your password?
            </Link>
          </p>
          {canRegister && (
            <p className="sans" style={{ margin: '8px 0 0', color: 'var(--dim)', fontSize: '0.84rem' }}>
              New here?{' '}
              <Link to="/account/register" style={{ color: 'var(--accent)', textDecoration: 'none' }}>
                Create an account
              </Link>
            </p>
          )}
        </div>
      }
    >
      <form onSubmit={stage === 'totp' ? onSubmitTotp : onSubmit}>
        {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>
            <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. Inside the app's Custom Tab
                this is also what trusts the device for future native sign-ins. */}
            <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>

        {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={ssoBtnStyle}>
                  <span style={{ display: 'inline-flex', width: 18, height: 18 }}>
                    <ProviderIcon icon={p.icon} size={18} />
                  </span>
                  Continue with {p.name}
                </button>
              ))}
            </div>
          </div>
        )}
      </form>

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

const ssoBtnStyle = {
  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)',
}
