'use client'

// ST-1007: Desktop-UI voor de QR-pairing-flow (M10).
//
// Klikt → POST /pair/start (cookie + body) → render QR die fragment-URL bevat
// → EventSource luistert naar /pair/stream/[id] met s4m_pair-cookie → bij
// approved-event POST /pair/claim → router.push('/dashboard').
//
// mobileSecret blijft in JS-memory en in het QR-fragment; wordt nooit naar
// de server gestuurd vanuit deze browser. desktopToken zit alleen in de
// HttpOnly s4m_pair-cookie. fetch en EventSource sturen die cookie automatisch
// mee binnen de Path=/api/auth/pair-scope.

import { useEffect, useRef, useState } from 'react'
import { useRouter } from 'next/navigation'
import { QRCodeSVG } from 'qrcode.react'
import { toast } from 'sonner'
import { Button } from '@/components/ui/button'

type Phase =
  | { kind: 'idle' }
  | { kind: 'starting' }
  | { kind: 'showing'; pairingId: string; qrUrl: string; expiresAt: number }
  | { kind: 'expired' }
  | { kind: 'claiming' }

interface StartResponse {
  pairingId: string
  mobileSecret: string
  expiresAt: string
  qrUrl: string
}

interface StreamMessage {
  op?: 'I' | 'U'
  status?: 'pending' | 'approved' | 'consumed' | 'cancelled'
  pairing_id?: string
}

export function QrLoginButton() {
  const router = useRouter()
  const [phase, setPhase] = useState<Phase>({ kind: 'idle' })
  const sseRef = useRef<EventSource | null>(null)
  const [secondsLeft, setSecondsLeft] = useState(0)

  async function start() {
    setPhase({ kind: 'starting' })
    try {
      const res = await fetch('/api/auth/pair/start', {
        method: 'POST',
        credentials: 'same-origin',
      })
      if (!res.ok) throw new Error(`pair/start ${res.status}`)
      const data = (await res.json()) as StartResponse
      setPhase({
        kind: 'showing',
        pairingId: data.pairingId,
        qrUrl: data.qrUrl,
        expiresAt: new Date(data.expiresAt).getTime(),
      })
    } catch {
      toast.error('Kon QR-code niet aanmaken — probeer opnieuw')
      setPhase({ kind: 'idle' })
    }
  }

  // Open SSE-stream zodra we in 'showing' zijn
  useEffect(() => {
    if (phase.kind !== 'showing') return

    const es = new EventSource(`/api/auth/pair/stream/${phase.pairingId}`, {
      withCredentials: true,
    })
    sseRef.current = es

    const onMessage = async (ev: MessageEvent) => {
      let data: StreamMessage
      try {
        data = JSON.parse(ev.data) as StreamMessage
      } catch {
        return
      }
      if (data.status !== 'approved') return

      // Approved! Sluit SSE en claim de sessie
      es.close()
      sseRef.current = null
      setPhase({ kind: 'claiming' })
      try {
        const res = await fetch('/api/auth/pair/claim', {
          method: 'POST',
          credentials: 'same-origin',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ pairingId: phase.pairingId }),
        })
        if (!res.ok) throw new Error(`pair/claim ${res.status}`)
        router.push('/dashboard')
      } catch {
        toast.error('Inloggen mislukt — probeer opnieuw')
        setPhase({ kind: 'idle' })
      }
    }

    const onError = () => {
      // EventSource probeert zelf opnieuw te verbinden bij netwerk-glitches.
      // Geen actie nodig tenzij we definitief willen falen.
    }

    // De server stuurt direct na connect een `event: state`-payload met de
    // huidige pairing-status (catch-up voor de race tussen pair/start en de
    // SSE-open: als de mobiel net daarvoor approvet komt de notify door
    // vóórdat onze LISTEN actief is en wordt 'ie verloren). EventSource
    // routeert events met `event: <name>` alleen naar listeners voor die
    // naam — niet naar 'message'. Dezelfde handler aan beide hangen vangt
    // de catch-up én reguliere notifies op.
    es.addEventListener('message', onMessage)
    es.addEventListener('state', onMessage as unknown as EventListener)
    es.addEventListener('error', onError)

    return () => {
      es.removeEventListener('message', onMessage)
      es.removeEventListener('state', onMessage as unknown as EventListener)
      es.removeEventListener('error', onError)
      es.close()
      sseRef.current = null
    }
  }, [phase, router])

  // Aftellen + auto-expire
  useEffect(() => {
    if (phase.kind !== 'showing') return

    const tick = () => {
      const remaining = Math.max(0, Math.ceil((phase.expiresAt - Date.now()) / 1000))
      setSecondsLeft(remaining)
      if (remaining === 0) {
        sseRef.current?.close()
        sseRef.current = null
        setPhase({ kind: 'expired' })
      }
    }

    tick()
    const id = setInterval(tick, 1000)
    return () => clearInterval(id)
  }, [phase])

  if (phase.kind === 'idle' || phase.kind === 'starting') {
    return (
      <Button
        type="button"
        variant="outline"
        className="w-full"
        onClick={start}
        disabled={phase.kind === 'starting'}
      >
        {phase.kind === 'starting' ? 'Bezig…' : 'Inloggen via mobiel'}
      </Button>
    )
  }

  if (phase.kind === 'expired') {
    return (
      <div className="space-y-3 text-center">
        <p className="text-muted-foreground text-sm">
          QR-code verlopen. Maak een nieuwe aan om opnieuw te proberen.
        </p>
        <Button type="button" variant="outline" className="w-full" onClick={start}>
          Vernieuwen
        </Button>
      </div>
    )
  }

  if (phase.kind === 'claiming') {
    return (
      <div className="text-center text-sm" aria-live="polite">
        Inloggen…
      </div>
    )
  }

  // phase.kind === 'showing'
  const minutes = Math.floor(secondsLeft / 60)
  const seconds = String(secondsLeft % 60).padStart(2, '0')

  return (
    <div className="space-y-3">
      <div className="bg-surface-container-low flex flex-col items-center gap-3 rounded-xl border border-border p-4">
        <QRCodeSVG
          value={phase.qrUrl}
          size={200}
          level="M"
          aria-label="QR-code voor inloggen via mobiel"
        />
        <p className="text-muted-foreground text-xs" aria-live="polite">
          Vervalt over {minutes}:{seconds}
        </p>
      </div>
      <details className="text-muted-foreground text-xs">
        <summary className="cursor-pointer">Werkt scannen niet? Toon link</summary>
        <p className="mt-2 break-all font-mono text-[11px]">{phase.qrUrl}</p>
      </details>
      <p className="text-muted-foreground text-center text-xs">
        Scan met een telefoon waar je al ingelogd bent.
      </p>
    </div>
  )
}
