'use client'

/**
 * AgentTestTab — Top-Level für den 5-URL-Test eines Specialist-Agents.
 * Sections:
 *   1. Agent-Wähler + 5 URL-Slots + Start-Button
 *   2. Methodik-Erklärung (was wir tun, warum)
 *   3. Live-Event-Log
 *   4. Pro Slot: SlotCard (siehe AgentSlotCard.tsx)
 */

import React, { useEffect, useMemo, useRef, useState } from 'react'

import type { AgentInfo, RunResult, SlotOutput, StreamEvent } from './_agentTypes'
import { AgentSlotCard } from './AgentSlotCard'

const STORAGE_KEY = 'agent-test-state-v1'
const MAX_SLOTS = 5

export function AgentTestTab() {
  const [agents, setAgents] = useState<AgentInfo[]>([])
  const [agentId, setAgentId] = useState<string>('')
  const [urls, setUrls] = useState<string[]>(['', '', '', '', ''])
  const [running, setRunning] = useState(false)
  const [runId, setRunId] = useState<string>('')
  const [events, setEvents] = useState<StreamEvent[]>([])
  const [result, setResult] = useState<RunResult | null>(null)
  const [error, setError] = useState<string>('')
  const eventSrcRef = useRef<EventSource | null>(null)

  // Restore state from localStorage
  useEffect(() => {
    try {
      const s = localStorage.getItem(STORAGE_KEY)
      if (s) {
        const parsed = JSON.parse(s)
        if (parsed.agentId) setAgentId(parsed.agentId)
        if (Array.isArray(parsed.urls)) {
          const padded = [...parsed.urls.slice(0, MAX_SLOTS),
            ...new Array(MAX_SLOTS).fill('')].slice(0, MAX_SLOTS)
          setUrls(padded)
        }
      }
    } catch { /* noop */ }
  }, [])
  useEffect(() => {
    try {
      localStorage.setItem(STORAGE_KEY,
        JSON.stringify({ agentId, urls }))
    } catch { /* quota */ }
  }, [agentId, urls])

  // Load agents
  useEffect(() => {
    fetch('/api/sdk/v1/specialist-agent/agents')
      .then(r => r.json())
      .then(d => {
        const list: AgentInfo[] = d.agents || []
        setAgents(list)
        if (list.length && !agentId) setAgentId(list[0].agent_id)
      })
      .catch(e => setError(`Agent-Liste fehlgeschlagen: ${e}`))
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  const startTest = async () => {
    setError('')
    setResult(null)
    setEvents([])
    const cleanUrls = urls.map(u => u.trim()).filter(Boolean)
    if (!agentId) { setError('Kein Agent ausgewählt.'); return }
    if (cleanUrls.length === 0) { setError('Mind. eine URL angeben.'); return }
    setRunning(true)
    try {
      const r = await fetch('/api/sdk/v1/specialist-agent/test/start', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ agent_id: agentId, urls: cleanUrls }),
      })
      if (!r.ok) {
        const j = await r.json().catch(() => ({}))
        throw new Error(j.error || `HTTP ${r.status}`)
      }
      const data = await r.json()
      setRunId(data.run_id)
      openStream(data.run_id)
      pollResult(data.run_id)
    } catch (e: any) {
      setError(e.message || String(e))
      setRunning(false)
    }
  }

  const openStream = (rid: string) => {
    try { eventSrcRef.current?.close() } catch { /* noop */ }
    const es = new EventSource(
      `/api/sdk/v1/specialist-agent/test/stream/${rid}`,
    )
    eventSrcRef.current = es
    es.onmessage = (ev) => {
      try {
        const data: StreamEvent = JSON.parse(ev.data)
        setEvents(prev => [...prev, data])
        if (data.type === 'stream_close' || data.type === 'run_complete') {
          try { es.close() } catch { /* noop */ }
        }
      } catch { /* noop */ }
    }
    es.onerror = () => { try { es.close() } catch { /* noop */ } }
  }

  const pollResult = async (rid: string) => {
    for (let i = 0; i < 360; i++) {
      try {
        const r = await fetch(
          `/api/sdk/v1/specialist-agent/run/${rid}/result`,
        )
        if (r.ok) {
          const d: RunResult = await r.json()
          if (d.finished) {
            setResult(d); setRunning(false); return
          }
        }
      } catch { /* noop */ }
      await new Promise(s => setTimeout(s, 2000))
    }
    setRunning(false)
  }

  const slotOutputs = useMemo(() => {
    if (!result) return []
    const items: { slot: string; output: SlotOutput }[] = []
    for (const slot of Object.keys(result.results)) {
      items.push({ slot, output: result.results[slot] })
    }
    return items.sort((a, b) => a.slot.localeCompare(b.slot))
  }, [result])

  const selectedAgent = agents.find(a => a.agent_id === agentId)

  return (
    <div className="space-y-4">
      <InputCard
        agents={agents}
        agentId={agentId}
        setAgentId={setAgentId}
        selectedAgent={selectedAgent}
        urls={urls}
        setUrls={setUrls}
        running={running}
        runId={runId}
        startTest={startTest}
        error={error}
      />

      <MethodikInfo />

      {running && events.length > 0 && <EventLog events={events} />}

      {slotOutputs.length > 0 && (
        <div className="space-y-3">
          {slotOutputs.map(({ slot, output }) => (
            <AgentSlotCard
              key={slot} slot={slot} output={output} runId={runId}
            />
          ))}
        </div>
      )}
    </div>
  )
}

function InputCard({
  agents, agentId, setAgentId, selectedAgent, urls, setUrls,
  running, runId, startTest, error,
}: {
  agents: AgentInfo[]
  agentId: string
  setAgentId: (s: string) => void
  selectedAgent?: AgentInfo
  urls: string[]
  setUrls: (urls: string[]) => void
  running: boolean
  runId: string
  startTest: () => void
  error: string
}) {
  return (
    <div className="rounded-lg border bg-white p-4 space-y-3 shadow-sm">
      <h2 className="text-lg font-semibold">Agent-Test (max. {MAX_SLOTS} URLs)</h2>
      <div className="flex flex-wrap gap-3 items-end">
        <div>
          <label className="block text-xs font-medium text-gray-600">Agent</label>
          <select
            value={agentId}
            onChange={e => setAgentId(e.target.value)}
            className="border rounded px-2 py-1 text-sm"
          >
            {agents.map(a => (
              <option key={a.agent_id} value={a.agent_id}>
                {a.agent_id} v{a.agent_version} ({a.mc_count} MCs)
              </option>
            ))}
          </select>
        </div>
        {selectedAgent && (
          <div className="text-xs text-gray-500">
            Doc-Type: <code>{selectedAgent.doc_type}</code>
          </div>
        )}
      </div>
      <div className="space-y-1">
        {urls.map((u, i) => (
          <div key={i} className="flex gap-2">
            <span className="text-xs font-mono text-gray-500 w-8 pt-1.5">
              URL{i + 1}
            </span>
            <input
              value={u}
              onChange={e => {
                const next = [...urls]; next[i] = e.target.value
                setUrls(next)
              }}
              placeholder="https://example.com/impressum"
              className="flex-1 border rounded px-2 py-1 text-sm font-mono"
            />
          </div>
        ))}
      </div>
      <div className="flex gap-2">
        <button
          onClick={startTest}
          disabled={running}
          className="bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white text-sm px-4 py-2 rounded"
        >
          {running ? 'Laufend...' : 'Test starten'}
        </button>
        {runId && (
          <span className="text-xs text-gray-500 self-center">
            Run-ID: <code>{runId}</code>
          </span>
        )}
      </div>
      {error && (
        <div className="bg-red-50 border-l-4 border-red-400 p-2 text-sm text-red-700">
          {error}
        </div>
      )}
    </div>
  )
}

function MethodikInfo() {
  return (
    <details className="rounded border bg-slate-50 px-3 py-2 text-xs text-gray-700">
      <summary className="cursor-pointer font-semibold">
        Methodik — wie geprüft wird
      </summary>
      <ol className="list-decimal ml-5 mt-2 space-y-1">
        <li>
          <strong>Pattern-Checks</strong> — deterministische Regex-Tests
          gegen Pflichtangaben-Schema (z.B. § 5 TMG/DDG). Schnell,
          reproduzierbar. <em>Hinweis:</em> diese Pattern-IDs (z.B.
          <code>IMP-MC-001</code>) sind <strong>interne Test-IDs</strong>,
          nicht die Master-Control-IDs aus der Datenbank. BreakPilot hat
          313k Atomic-Controls → 13.588 dedup. Master-Controls; davon
          ~1.778 für dieses Compliance-Agent-Tool ausgewählt. Die formale
          Verknüpfung Pattern-Check → Master-Control folgt in einem
          späteren Schritt (Sprint 1.12).
        </li>
        <li>
          <strong>Knowledge-Base</strong> — kuratierte Patterns aus
          anonymisierten Mandanten-FAQs.
        </li>
        <li>
          <strong>Auto-Learning-Pattern-Library</strong> — Labels die
          der LLM-Validator gefunden hat (z.B. „Telefonnr." statt
          „Telefon") werden persistiert. Beim nächsten Run sind sie
          deterministisch erkennbar — der LLM wird seltener gerufen.
        </li>
        <li>
          <strong>Semantic-Validator (LLM)</strong> — nur bei
          missing-Pflichtangabe: ein Aufruf des Self-Hosted-LLM
          (<code>qwen3.5:35b-a3b</code> auf macmini) prüft ob die
          Angabe doch da ist, nur unter abweichendem Label. Bei
          Treffer wird HIGH→LOW demoted und „Umbenennen zu Standard"
          empfohlen.
        </li>
        <li>
          <strong>LLM-Eskalation (Fallback)</strong> — wenn der
          Validator unsicher bleibt: OVH 120b, dann anonymisierter
          Claude-Cloud-Call. Aktuell deaktiviert (OVH-Key leer).
        </li>
        <li>
          <strong>Cross-Placement-Agent</strong> — erkennt deplatzierten
          Content (Copyright, Disclaimer, WEEE im Impressum) +
          empfiehlt Footer-Reiter „Legal".
        </li>
      </ol>
      <p className="mt-2 italic text-gray-500">
        Disclaimer: keine Aussagen wie „rechtssicher" oder „konform" —
        nur Findings + Empfehlungen + Herleitung. Verbotene Begriffe
        werden vom Linter aus Agent-Outputs entfernt.
      </p>
    </details>
  )
}

function EventLog({ events }: { events: StreamEvent[] }) {
  return (
    <div className="rounded border bg-gray-50 p-3 max-h-48 overflow-y-auto">
      <div className="text-xs font-mono space-y-0.5">
        {events.slice(-30).map((ev, i) => (
          <div key={i}>
            <span className="text-gray-400">[{ev.type}]</span>{' '}
            {ev.slot && <span className="text-blue-600">{ev.slot}</span>}{' '}
            {ev.severity && (
              <span className={severityColor(ev.severity)}>{ev.severity}</span>
            )}{' '}
            {ev.title || ev.error || ev.label || ev.model || ev.url || ''}
            {ev.word_count !== undefined && (
              <span className="text-gray-500">
                {' '}({ev.word_count} Wörter)
              </span>
            )}
          </div>
        ))}
      </div>
    </div>
  )
}

function severityColor(sev: string) {
  return sev === 'HIGH' ? 'text-red-600 font-semibold' :
         sev === 'MEDIUM' ? 'text-amber-600 font-semibold' :
         sev === 'LOW' ? 'text-blue-600' : 'text-gray-600'
}
