'use client'

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

import { useI18n } from '@/components/providers/i18n-provider'
import { Button, Input, Notice } from '@/components/ui/controls'
import { Badge, Panel, PanelBody, PanelHeader } from '@/components/ui/display'
import { api, ApiClientError } from '@/lib/api/client'
import { cn } from '@/lib/cn'

interface Node {
  code: string
  kind: string
  label: string
  labelAr: string
  detail: string
  detailAr: string
  regionCode: string | null
  weight: number
  refType: string
  refId: string
}

interface Edge {
  from: string
  to: string
  relation: string
  relationAr: string
  weight: number
}

const KIND_COLOR: Record<string, string> = {
  concept: '#38bdf8',
  action: '#34d399',
  region: '#a78bfa',
  asset: '#fbbf24',
  incident: '#f87171',
  pattern: '#f472b6',
  alert: '#fb923c',
  prediction: '#22d3ee',
  site: '#94a3b8',
  weather: '#60a5fa',
  role: '#c084fc',
}

const VIEW = 720
const CENTRE = VIEW / 2

/**
 * Graph Explorer (§17).
 *
 * The layout is a deterministic radial one — focus at the centre, neighbours placed by
 * index around it — rather than a force simulation. Two reasons: the same node always
 * draws the same picture, which matters when a presenter walks back to it; and a physics
 * loop on a page that already re-renders on a live tick is a battery bill with no reader
 * benefit.
 */
export function KnowledgeExplorer({
  initialFocus,
  initialNodes,
  initialEdges,
}: {
  initialFocus: Node | null
  initialNodes: Node[]
  initialEdges: Edge[]
}) {
  const { t, locale } = useI18n()
  const ar = locale === 'ar'

  const [focus, setFocus] = useState<Node | null>(initialFocus)
  const [nodes, setNodes] = useState<Node[]>(initialNodes)
  const [edges, setEdges] = useState<Edge[]>(initialEdges)
  const [query, setQuery] = useState('')
  const [results, setResults] = useState<Node[] | null>(null)
  const [error, setError] = useState<string | null>(null)
  const [busy, setBusy] = useState(false)
  const [selected, setSelected] = useState<Node | null>(initialFocus)

  const loadNeighbourhood = async (code: string) => {
    setBusy(true)
    setError(null)
    try {
      const data = await api.get<{ focus: Node | null; nodes: Node[]; edges: Edge[] }>(
        `/api/knowledge-graph?focus=${encodeURIComponent(code)}&depth=1`,
      )
      setFocus(data.focus)
      setSelected(data.focus)
      setNodes(data.nodes)
      setEdges(data.edges)
      setResults(null)
    } catch (cause) {
      setError(cause instanceof ApiClientError ? (ar ? cause.failure.messageAr : cause.failure.message) : String(cause))
    } finally {
      setBusy(false)
    }
  }

  const runSearch = async () => {
    const needle = query.trim()
    if (!needle) return
    setBusy(true)
    setError(null)
    try {
      const data = await api.get<{ results: Node[] }>(
        `/api/knowledge-graph?q=${encodeURIComponent(needle)}`,
      )
      setResults(data.results)
    } catch (cause) {
      setError(cause instanceof ApiClientError ? (ar ? cause.failure.messageAr : cause.failure.message) : String(cause))
    } finally {
      setBusy(false)
    }
  }

  // Positions are derived from the node order, so the same neighbourhood always draws
  // identically. The radius grows with the count so a busy node spreads out instead of
  // overlapping itself.
  const layout = useMemo(() => {
    const others = nodes.filter((node) => node.code !== focus?.code)
    const radius = Math.min(CENTRE - 70, 140 + others.length * 5)
    const positions = new Map<string, { x: number; y: number }>()
    if (focus) positions.set(focus.code, { x: CENTRE, y: CENTRE })
    others.forEach((node, index) => {
      const angle = (index / Math.max(1, others.length)) * Math.PI * 2 - Math.PI / 2
      positions.set(node.code, {
        x: CENTRE + Math.cos(angle) * radius,
        y: CENTRE + Math.sin(angle) * radius,
      })
    })
    return positions
  }, [nodes, focus])

  useEffect(() => {
    setSelected(focus)
  }, [focus])

  return (
    <div className="grid gap-4 lg:grid-cols-[1fr_20rem]">
      <Panel>
        <PanelHeader
          title={focus ? (ar ? focus.labelAr : focus.label) : t('knowledge.title')}
          subtitle={focus ? `${t('knowledge.focus')}: ${focus.code}` : t('knowledge.selectHint')}
          action={<Badge tone="muted">{nodes.length}</Badge>}
        />
        <PanelBody className="pt-0">
          <div className="overflow-x-auto">
            <svg
              viewBox={`0 0 ${VIEW} ${VIEW}`}
              className="mx-auto h-auto w-full max-w-[44rem]"
              role="img"
              aria-label={t('knowledge.title')}
            >
              {edges.map((edge, index) => {
                const from = layout.get(edge.from)
                const to = layout.get(edge.to)
                if (!from || !to) return null
                return (
                  <line
                    key={`${edge.from}-${edge.to}-${index}`}
                    x1={from.x}
                    y1={from.y}
                    x2={to.x}
                    y2={to.y}
                    stroke="currentColor"
                    strokeWidth={Math.max(0.6, edge.weight * 0.4)}
                    className="text-border-strong"
                    opacity={0.65}
                  />
                )
              })}

              {nodes.map((node) => {
                const position = layout.get(node.code)
                if (!position) return null
                const isFocus = node.code === focus?.code
                const radius = isFocus ? 16 : 7 + Math.min(6, node.weight)
                return (
                  <g key={node.code}>
                    <circle
                      cx={position.x}
                      cy={position.y}
                      r={radius}
                      fill={KIND_COLOR[node.kind] ?? '#94a3b8'}
                      fillOpacity={isFocus ? 0.9 : 0.62}
                      stroke={KIND_COLOR[node.kind] ?? '#94a3b8'}
                      strokeWidth={isFocus ? 3 : 1}
                      className="cursor-pointer"
                      onClick={() => setSelected(node)}
                      onDoubleClick={() => void loadNeighbourhood(node.code)}
                    >
                      <title>{ar ? node.labelAr : node.label}</title>
                    </circle>
                    <text
                      x={position.x}
                      y={position.y + radius + 12}
                      textAnchor="middle"
                      className="pointer-events-none fill-current text-[10px] text-text-muted"
                    >
                      {(ar ? node.labelAr : node.label).slice(0, 22)}
                    </text>
                  </g>
                )
              })}
            </svg>
          </div>
          <p className="mt-2 text-[10px] text-text-faint">{t('knowledge.selectHint')}</p>
        </PanelBody>
      </Panel>

      <div className="min-w-0 space-y-4">
        <Panel>
          <PanelHeader title={t('knowledge.search')} />
          <PanelBody className="space-y-3 pt-0">
            <div className="flex gap-2">
              <Input
                value={query}
                onChange={(event) => setQuery(event.target.value)}
                onKeyDown={(event) => {
                  if (event.key === 'Enter') void runSearch()
                }}
                placeholder={t('knowledge.searchPlaceholder')}
                aria-label={t('knowledge.search')}
              />
              <Button size="sm" onClick={runSearch} loading={busy}>
                {t('knowledge.search')}
              </Button>
            </div>

            {error ? <Notice tone="danger">{error}</Notice> : null}

            {results ? (
              results.length === 0 ? (
                <p className="text-xs text-text-muted">{t('knowledge.noResults')}</p>
              ) : (
                <ul className="max-h-64 space-y-1 overflow-y-auto">
                  {results.map((node) => (
                    <li key={node.code}>
                      <button
                        type="button"
                        onClick={() => void loadNeighbourhood(node.code)}
                        className="w-full rounded-lg px-2 py-1.5 text-start text-xs text-text-muted transition-colors hover:bg-surface-2 hover:text-text"
                      >
                        <span
                          aria-hidden
                          className="me-2 inline-block size-2 rounded-full align-middle"
                          style={{ backgroundColor: KIND_COLOR[node.kind] ?? '#94a3b8' }}
                        />
                        {ar ? node.labelAr : node.label}
                        <span className="ms-1 text-[10px] text-text-faint">
                          {t(`knowledge.kind.${node.kind}`)}
                        </span>
                      </button>
                    </li>
                  ))}
                </ul>
              )
            ) : null}
          </PanelBody>
        </Panel>

        {selected ? (
          <Panel>
            <PanelHeader
              title={ar ? selected.labelAr : selected.label}
              subtitle={t(`knowledge.kind.${selected.kind}`)}
              action={
                <span
                  aria-hidden
                  className="size-3 shrink-0 rounded-full"
                  style={{ backgroundColor: KIND_COLOR[selected.kind] ?? '#94a3b8' }}
                />
              }
            />
            <PanelBody className="space-y-3 pt-0">
              <p className="text-xs leading-relaxed text-text-muted">
                {ar ? selected.detailAr : selected.detail}
              </p>
              <div>
                <p className="text-[10px] uppercase tracking-wide text-text-faint">
                  {t('knowledge.relations')}
                </p>
                <ul className="mt-1.5 space-y-1">
                  {edges
                    .filter((edge) => edge.from === selected.code || edge.to === selected.code)
                    .slice(0, 12)
                    .map((edge, index) => {
                      const outgoing = edge.from === selected.code
                      const otherCode = outgoing ? edge.to : edge.from
                      const other = nodes.find((node) => node.code === otherCode)
                      return (
                        <li key={`${edge.from}-${edge.to}-${index}`} className="text-[11px] text-text-muted">
                          <span className={cn(outgoing ? 'text-brand' : 'text-accent')}>
                            {ar ? edge.relationAr : edge.relation}
                          </span>{' '}
                          {other ? (ar ? other.labelAr : other.label) : otherCode}
                        </li>
                      )
                    })}
                </ul>
              </div>
              <Button
                size="sm"
                variant="secondary"
                onClick={() => void loadNeighbourhood(selected.code)}
                loading={busy}
              >
                {t('knowledge.focus')}
              </Button>
            </PanelBody>
          </Panel>
        ) : null}
      </div>
    </div>
  )
}
