/**
 * Runs the /preflight sequence in front of the user before a wizard run
 * actually starts. Three checks + one soft-fail (PowerPoint) gated on the
 * chosen output format.
 */
import { useEffect, useState } from 'react'
import { CheckCircle2, Loader2, Server, Cpu, Sparkles, Presentation, Film, XCircle, AlertTriangle } from 'lucide-react'
import { api } from '../api/client'
import type { OutputFormat, PreflightResponse } from '../api/types'
import { useFocusTrap } from '../hooks/useFocusTrap'

type CheckStatus = 'idle' | 'running' | 'pass' | 'fail' | 'warn'

interface CheckRow {
  key: keyof PreflightResponse['checks']
  label: string
  icon: typeof Server
}

const ROWS: CheckRow[] = [
  { key: 'platform', label: 'Platform', icon: Cpu },
  { key: 'backend', label: 'Backend connection', icon: Server },
  { key: 'ai_config', label: 'AI configuration', icon: Sparkles },
  { key: 'powerpoint', label: 'PowerPoint availability', icon: Presentation },
  { key: 'video_engine', label: 'Video engine', icon: Film },
]

function statusDot(s: CheckStatus) {
  if (s === 'running') return <Loader2 size={16} className="animate-spin text-slate-400" />
  if (s === 'pass') return <CheckCircle2 size={16} className="text-brand-600" />
  if (s === 'warn') return <AlertTriangle size={16} className="text-amber-500" />
  if (s === 'fail') return <XCircle size={16} className="text-rose-500" />
  return <span className="inline-block h-4 w-4 rounded-full border border-slate-300 dark:border-white/15" />
}

interface PreflightModalProps {
  outputFormat: OutputFormat
  onCancel: () => void
  onProceed: () => void
}

const OUTPUT_LABELS: Record<OutputFormat, string> = {
  html: 'HTML file',
  images: 'screenshots',
  pptx: 'PowerPoint deck',
  video: 'MP4 video',
}

export default function PreflightModal({ outputFormat, onCancel, onProceed }: PreflightModalProps) {
  const [data, setData] = useState<PreflightResponse | null>(null)
  const [loadingKey, setLoadingKey] = useState<keyof PreflightResponse['checks'] | null>('platform')
  const [error, setError] = useState<string | null>(null)
  const dialogRef = useFocusTrap<HTMLDivElement>(true)

  useEffect(() => {
    let cancelled = false
    ;(async () => {
      try {
        // Walk through the visual rows so the user sees each one light up in
        // sequence, even though the backend reports them all at once.
        // Always request fresh — the user is gating a run behind this
        // modal and needs the actual current state of the backend, not a
        // 30-second-stale cached result.
        const res = await api.preflight({ fresh: true })
        if (cancelled) return
        for (const row of ROWS) {
          setLoadingKey(row.key)
          await new Promise((r) => setTimeout(r, 350))
          if (cancelled) return
        }
        setLoadingKey(null)
        setData(res)
      } catch (e) {
        if (cancelled) return
        setError(e instanceof Error ? e.message : String(e))
        setLoadingKey(null)
      }
    })()
    return () => {
      cancelled = true
    }
  }, [])

  // Escape key closes the modal.
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onCancel()
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  }, [onCancel])

  const needsPpt = outputFormat === 'pptx'
  const needsVideo = outputFormat === 'video'
  const pptOk = data?.checks.powerpoint.ok ?? false
  const videoEngineOk = data?.checks.video_engine?.ok ?? pptOk
  const aiOk = data?.checks.ai_config.ok ?? false
  const backendOk = !!data
  const blocked = !data
    ? false
    : !backendOk || !aiOk || (needsPpt && !pptOk) || (needsVideo && !videoEngineOk)

  const rowStatus = (key: keyof PreflightResponse['checks']): CheckStatus => {
    if (!data) {
      if (loadingKey === key) return 'running'
      const idx = ROWS.findIndex((r) => r.key === loadingKey)
      const myIdx = ROWS.findIndex((r) => r.key === key)
      if (idx >= 0 && myIdx < idx) return 'pass'
      return 'idle'
    }
    const c = data.checks[key]
    if (!c) return 'idle'
    if (c.ok) return 'pass'
    // Soft-fail: PowerPoint only matters for PPTX export; video_engine
    // only matters for MP4. Anything else is always hard-required.
    if (key === 'powerpoint' && !needsPpt) return 'warn'
    if (key === 'video_engine' && !needsVideo) return 'warn'
    return 'fail'
  }

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
      <div
        className="absolute inset-0 bg-slate-950/40 backdrop-blur-sm"
        onClick={onCancel}
        aria-hidden
      />
      <div
        ref={dialogRef}
        role="dialog"
        aria-modal="true"
        aria-labelledby="preflight-title"
        tabIndex={-1}
        className="relative w-full max-w-md rounded-xl border border-slate-200 bg-white p-6 shadow-lg dark:border-white/10 dark:bg-slate-900"
      >
        <h2
          id="preflight-title"
          className="font-display text-lg font-semibold text-slate-900 dark:text-slate-50"
        >
          Pre-flight checks
        </h2>
        <p className="mt-1 text-sm text-slate-600 dark:text-slate-400">
          Verifying the runtime can actually produce{' '}
          <span className="font-medium">{OUTPUT_LABELS[outputFormat]}</span>.
        </p>

        <ul className="mt-5 space-y-3">
          {ROWS.map((row) => {
            const s = rowStatus(row.key)
            const detail = data?.checks[row.key]?.detail ?? ''
            const Icon = row.icon
            return (
              <li key={row.key} className="flex items-start gap-3">
                <div className="mt-0.5 flex h-5 w-5 items-center justify-center">{statusDot(s)}</div>
                <div className="min-w-0 flex-1">
                  <div className="flex items-center gap-2">
                    <Icon size={14} className="text-slate-400" />
                    <span className="text-sm font-medium text-slate-800 dark:text-slate-100">
                      {row.label}
                    </span>
                    {row.key === 'powerpoint' && !needsPpt && (
                      <span className="text-[10px] uppercase tracking-wider text-slate-400">
                        optional for {OUTPUT_LABELS[outputFormat]}
                      </span>
                    )}
                  </div>
                  {detail && (
                    <p className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">{detail}</p>
                  )}
                </div>
              </li>
            )
          })}
        </ul>

        {error && (
          <div className="mt-4 rounded-md border border-rose-200 bg-rose-50 p-3 text-xs text-rose-700 dark:border-rose-500/30 dark:bg-rose-500/10 dark:text-rose-200">
            Preflight request failed: {error}
          </div>
        )}

        {data && blocked && (
          <div className="mt-4 rounded-md border border-rose-200 bg-rose-50 p-3 text-xs text-rose-700 dark:border-rose-500/30 dark:bg-rose-500/10 dark:text-rose-200">
            {!aiOk && <p>AI config is missing — edit <code>backend/config/config.py</code> and restart the backend.</p>}
            {needsPpt && !pptOk && (
              <p className="mt-1">
                Output is <strong>{OUTPUT_LABELS[outputFormat]}</strong> but PowerPoint isn't
                available. Go back to step 1 and pick <em>HTML file</em> or{' '}
                <em>screenshots</em>, or install PowerPoint on a Windows host.
              </p>
            )}
          </div>
        )}

        <div className="mt-6 flex items-center justify-end gap-2">
          <button type="button" className="btn-secondary" onClick={onCancel}>
            Cancel
          </button>
          <button
            type="button"
            className="btn-primary"
            disabled={!data || blocked}
            onClick={onProceed}
          >
            Proceed
          </button>
        </div>
      </div>
    </div>
  )
}
