import { useCallback, useEffect, useState } from 'react'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { useAuth } from '../../contexts/AuthContext.jsx'
import { api } from '../../api/client.js'
import PlayerShell, { honeypotStyle } from './PlayerShell.jsx'
import Slot from '../../modules/Slot.jsx'
import { extensionFor } from '../../modules/registry.js'

// Public, token-gated invite acceptance (/invite/:token). Validates the invite,
// lets the invitee set a username + password (their email + role are pre-assigned),
// creates the account at that role and logs them in.
//
// For a PLAYER invite there may then be one more step, supplied by an installed
// module through the `player.invite.accepted` slot: core rendered a UO
// game-account form here itself until Phase 3 slice 3, reading a
// `gameAccountSignup` flag out of its own settings and posting to a shard route.
// Neither of those is core's. What core keeps is the shell, the skip control and
// the destination; whether there is a step at all is the module's call, made
// from data core does not have.
export default function AcceptInvite() {
  const { token } = useParams()
  const navigate = useNavigate()
  const { refresh } = useAuth()

  const [invite, setInvite] = useState(null) // fields email and role
  const [loadErr, setLoadErr] = useState('')

  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [company, setCompany] = useState('') // honeypot
  const [error, setError] = useState('')
  const [busy, setBusy] = useState(false)
  const [accepted, setAccepted] = useState(false)

  useEffect(() => {
    let active = true
    api.getInvite(token)
      .then((iv) => active && setInvite(iv))
      .catch((err) => active && setLoadErr(err.status === 404 ? 'This invitation is invalid or has expired.' : 'Could not load this invitation.'))
    return () => { active = false }
  }, [token])

  const dest = invite && invite.role === 'player' ? '/player' : '/admin'

  // Whether anything is installed that wants the post-acceptance step. Read
  // rather than rendered blind because it decides a NAVIGATION, not just what
  // appears: with nothing filled there is no screen to show, so the invitee goes
  // straight to their destination. This is the one legitimate reason to ask
  // whether a slot is filled — the answer changes control flow, not decoration
  // (decoration goes inside `<Slot wrap>`, which is why `hasExtension` is gone).
  const hasNextStep = Boolean(extensionFor('player.invite.accepted'))
  const finish = useCallback(() => navigate('/player', { replace: true }), [navigate])

  async function onSubmit(e) {
    e.preventDefault()
    setError('')
    if (username.trim().length < 3) return setError('Username must be at least 3 characters.')
    if (password.length < 8) return setError('Password must be at least 8 characters.')
    setBusy(true)
    try {
      await api.acceptInvite(token, username.trim(), password, { company })
      await refresh() // pull the freshly-issued session into context
      setAccepted(true)
      // Staff invites go straight in, and so does a player invite when nothing
      // is installed that has a step to offer.
      if (!(invite.role === 'player' && hasNextStep)) navigate(dest, { replace: true })
    } catch (err) {
      if (err.status === 409) setError('That username is already taken, or the invite was already used.')
      else if (err.status === 404) setError('This invitation is invalid or has expired.')
      else if (err.status === 400) setError(err.message || 'Please check your details and try again.')
      else setError('Could not accept the invitation right now.')
      setBusy(false)
    }
  }

  // ── Loading / invalid ─────────────────────────────────────────────────────
  if (loadErr) {
    return (
      <PlayerShell subtitle="Invitation">
        <p className="sans" style={{ margin: 0, color: 'var(--muted)', textAlign: 'center', lineHeight: 1.6 }}>{loadErr}</p>
        <p className="sans" style={{ textAlign: 'center', margin: '16px 0 0' }}>
          <Link to="/account/login" style={{ color: 'var(--accent)', textDecoration: 'none' }}>Go to sign in</Link>
        </p>
      </PlayerShell>
    )
  }
  if (!invite) {
    return (
      <PlayerShell subtitle="Invitation">
        <div style={{ display: 'grid', placeItems: 'center', padding: 20 }}><span className="spin" /></div>
      </PlayerShell>
    )
  }

  // ── Accepted: a module's optional next step (player invites) ───────────────
  //
  // Only reachable when the slot is filled — `onSubmit` navigates away otherwise
  // — so there is no empty-shell case to guard here.
  //
  // The subtitle is core's and says nothing about what the step is: naming it
  // would be core describing content it does not own, and the wrong description
  // is worse than a general one. "Skip" stays core's too, because where it goes
  // is core's decision, and it is rendered outside the slot deliberately — an
  // extension that throws must not take the way out with it.
  if (accepted) {
    return (
      <PlayerShell subtitle="One more step">
        <Slot name="player.invite.accepted" onDone={finish} />
        <p className="sans" style={{ textAlign: 'center', margin: '18px 0 0' }}>
          <button type="button" onClick={finish} className="btn" style={{ background: 'none', border: 'none', color: 'var(--accent)', cursor: 'pointer' }}>
            Skip for now →
          </button>
        </p>
      </PlayerShell>
    )
  }

  // ── Accept form ────────────────────────────────────────────────────────────
  return (
    <PlayerShell subtitle="Accept your invitation">
      <p className="sans" style={{ marginTop: 0, marginBottom: 18, color: 'var(--muted)', fontSize: '0.88rem', lineHeight: 1.6 }}>
        You’ve been invited as <strong style={{ color: 'var(--head)' }}>{invite.role}</strong>
        {invite.email ? <> for <strong style={{ color: 'var(--head)' }}>{invite.email}</strong></> : null}. Choose a username and password to finish.
      </p>
      <form onSubmit={onSubmit}>
        <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="new-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>

        {error && <p className="sans" style={{ margin: '0 0 14px', color: '#d98b84', fontSize: '0.85rem', textAlign: 'center' }}>{error}</p>}

        <button type="submit" disabled={busy} className="btn btn-primary" style={{ display: 'block', width: '100%', borderRadius: 8, padding: 12, textAlign: 'center' }}>
          {busy ? 'Creating…' : 'Accept & create account'}
        </button>
      </form>
    </PlayerShell>
  )
}
