"use client"

import { useState, useRef, useEffect, useCallback, useMemo } from "react"
import { PageHeader } from "@/components/page-header"
import { Button } from "@/components/ui/button"
import { servers as mockServers } from "@/lib/data"
import { Flag } from "@/components/flag"
import { useDataSource } from "@/lib/data-source"
import {
  TrashIcon, RefreshCwIcon, CircleIcon, Loader2Icon,
} from "lucide-react"
import { cn } from "@/lib/utils"

// ─── types ────────────────────────────────────────────────────────────────────

/** Unified server representation for the terminal sidebar */
interface TermServer {
  uid:          string              // unique key (mock id or String(backend id))
  backendId:    number | null       // null in mock mode
  name:         string
  host:         string
  country:      string
  status:       "online" | "offline" | "degraded" | null
  enabled:      boolean
  rosVersion:   string | null
  identityName: string | null
}

interface TermLine {
  id:   number
  kind: "output" | "prompt" | "error" | "info"
  text: string
}

// ─── mock CLI responses ───────────────────────────────────────────────────────

type CmdFn = (args: string[], serverName: string) => string

const MOCK_COMMANDS: Record<string, CmdFn> = {
  "ip address print": () =>
    "Flags: X - disabled, I - invalid, D - dynamic\n" +
    " #   ADDRESS            NETWORK         INTERFACE\n" +
    "  0     10.0.0.1/24       10.0.0.0        ether1-wan\n" +
    "  1     192.168.88.1/24   192.168.88.0    bridge-lan\n" +
    "  2     10.200.0.1/30     10.200.0.0      gre-msk-spb\n" +
    "  3     10.200.0.5/30     10.200.0.4      gre-msk-fra\n" +
    "  4 D   169.254.0.0/16    169.254.0.0     lo0",

  "ip route print": () =>
    "Flags: A - active, D - dynamic, C - connect, S - static, b - bgp, o - ospf\n" +
    " #      DST-ADDRESS        PREF-SRC        GATEWAY          DIST\n" +
    "  0 ADS  0.0.0.0/0                         10.0.0.254        1\n" +
    "  1 Ab   1.1.1.0/24                         10.0.1.1          20\n" +
    "  2 Ab   8.8.8.0/24                         10.0.1.1          20\n" +
    "  3 ADC  10.0.0.0/24       10.0.0.1                           0\n" +
    "  4 ADC  10.200.0.0/30     10.200.0.1                         0",

  "interface print": () =>
    "Flags: X - disabled, D - dynamic, R - running\n" +
    " #   NAME                TYPE         MTU\n" +
    "  0 R  ether1-wan           ether        1500\n" +
    "  1 R  ether2-lan           ether        1500\n" +
    "  2 R  bridge-lan           bridge       1500\n" +
    "  3 R  gre-msk-spb          gre          1476\n" +
    "  4 R  gre-msk-fra          gre          1476\n" +
    "  5    gre-msk-ams          gre          1476",

  "system resource print": () =>
    "                  uptime: 12d 4h 22m 18s\n" +
    "                 version: 7.14.3 (stable)\n" +
    "             free-memory: 512.0 MiB\n" +
    "            total-memory: 2048.0 MiB\n" +
    "                     cpu: Intel(R) Xeon(R)\n" +
    "               cpu-count: 4\n" +
    "           cpu-frequency: 3200 MHz\n" +
    "                cpu-load: 12%\n" +
    "          free-hdd-space: 8192.0 MiB\n" +
    "         total-hdd-space: 16384.0 MiB\n" +
    "       architecture-name: x86\n" +
    "              board-name: CHR\n" +
    "                platform: MikroTik",

  "system identity print": (_, n) => `  name: ${n}`,

  "ip firewall filter print": () =>
    "Flags: X - disabled, I - invalid, D - dynamic\n\n" +
    "  0  \n      ;;; Accept established connections\n" +
    "      chain=input action=accept connection-state=established,related\n\n" +
    "  1  \n      ;;; YouTube bypass traffic\n" +
    "      chain=forward action=accept src-address-list=youtube-bypass protocol=tcp dst-port=443\n" +
    "      packets=188421 bytes=14283698176\n\n" +
    "  2  \n      ;;; Block SSH from WAN\n" +
    "      chain=input action=drop in-interface=ether1-wan protocol=tcp dst-port=22\n" +
    "      packets=882412 bytes=54618112",

  "routing bgp session print": () =>
    "Flags: E - established\n" +
    " #   NAME                    REMOTE-AS  REMOTE-ADDRESS         STATE        UPTIME\n" +
    "  0 E peer-spb-01             65002      10.0.1.1               established  12d 4h 22m\n" +
    "  1 E peer-fra-01             65003      10.0.2.1               established  9d 12h 11m\n" +
    "  2   peer-ams-01             65004      10.0.3.1               active       —",

  "routing ospf neighbor print": () =>
    "Flags: V - virtual\n" +
    " #   ROUTER-ID        STATE    CHANGES  ADJACENCY       INTERFACE\n" +
    "  0  10.0.0.7         Full     8        12d 4h 18m      gre-msk-spb\n" +
    "  1  10.0.1.1         Full     3        9d 11h 42m      gre-msk-fra",

  "routing bfd session print": () =>
    " #   LOCAL-ADDRESS               REMOTE-ADDRESS              STATE    UPTIME\n" +
    "  0  10.200.0.1%gre-msk-spb      10.200.0.2%gre-msk-spb      up       12d 4h\n" +
    "  1  10.200.1.1%gre-msk-fra      10.200.1.2%gre-msk-fra      up       9d 11h",

  "log print": () =>
    "may/01 08:14:22 ospf,debug,packet     GRE-MSK-FRA: hello received, RouterID: 10.0.2.1\n" +
    "may/01 08:14:21 bgp,debug             peer-spb-01 sending UPDATE\n" +
    "may/01 08:14:18 system,info           user admin logged from 10.10.0.5\n" +
    "may/01 08:12:44 firewall,info         forward: in:ether2-lan out:gre-msk-fra proto TCP\n" +
    "may/01 08:11:03 script,info           backup-script: backup saved to /backup/mt.rsc\n" +
    "may/01 08:09:17 system,warning        cpu load is 85% on cpu0",

  "ping": (args) => {
    const host  = args[0] || "8.8.8.8"
    const count = parseInt(args.find(a => a.startsWith("count="))?.split("=")[1] || "4")
    const rtt   = Math.round(10 + Math.random() * 50)
    const lines = [`PING ${host}`]
    for (let i = 0; i < Math.min(count, 5); i++) {
      const t = rtt + Math.round((Math.random() - 0.5) * 8)
      lines.push(`  seq=${i} ttl=56 time=${t}ms`)
    }
    lines.push(`  sent=${count} received=${count} packet-loss=0%`)
    return lines.join("\n")
  },
}

function mockResolve(raw: string, serverName: string): string {
  const trimmed = raw.trim()
  if (!trimmed) return ""

  if (trimmed === "?" || trimmed === "help")
    return "ip address print | ip route print | interface print\n" +
           "system resource print | ip firewall filter print\n" +
           "routing bgp session print | routing ospf neighbor print\n" +
           "routing bfd session print | log print | ping <host>"

  if (trimmed === "clear" || trimmed === "/clear") return "__CLEAR__"
  if (trimmed === "quit"  || trimmed === "exit")   return "Connection closed."

  const key = Object.keys(MOCK_COMMANDS).find(k =>
    trimmed === k || trimmed.toLowerCase().startsWith(k + " ")
  )
  if (key) {
    const rest = trimmed.slice(key.length).trim().split(/\s+/)
    return MOCK_COMMANDS[key](rest, serverName)
  }

  return `bad command name ${trimmed.split(" ")[0]} (line 1 column 1)`
}

// ─── MOTD builders ────────────────────────────────────────────────────────────

function mockMotd(server: TermServer): string {
  return [
    "",
    "  MMM      MMM       KKK                          TTTTTTTTTTT      KKK",
    "  MMM MMMM MMM  III  KKK  KKK  RRR   OOOOOO  TTT     TTT      II  KKK  KKK",
    "  MMM      MMM  III  KKK  KKK  RRR   RRROOOOO  TTT     TTT    II  KKK  KKK",
    "",
    `  MikroTik RouterOS ${server.rosVersion ?? "7.14.3"}   (c) 1999-${new Date().getFullYear()}       https://www.mikrotik.com/`,
    "",
    "[?] Gives the list of available commands",
    "[Tab] Completes the command/word",
    "[/] Move up to base level",
    "[..] Move up one level",
    "",
  ].join("\n")
}

function liveMotd(server: TermServer): string {
  return [
    "",
    `  MikroTik RouterOS ${server.rosVersion ?? "7.x"}   (c) 1999-${new Date().getFullYear()}       https://www.mikrotik.com/`,
    "",
    `  Connected to ${server.name}  (${server.host})`,
    "",
    "[?] Gives the list of available commands — type help or ?",
    "[Tab] Completes the command/word",
    "[/] Move up to base level",
    "",
  ].join("\n")
}

// ─── terminal component ───────────────────────────────────────────────────────

function Terminal({
  server,
  isLive,
  backendUrl,
}: {
  server: TermServer
  isLive: boolean
  backendUrl: string
}) {
  const [lines,     setLines]     = useState<TermLine[]>([])
  const [input,     setInput]     = useState("")
  const [history,   setHistory]   = useState<string[]>([])
  const [histIdx,   setHistIdx]   = useState(-1)
  const [idCtr,     setIdCtr]     = useState(0)
  const [executing, setExecuting] = useState(false)

  const inputRef  = useRef<HTMLInputElement>(null)
  const scrollRef = useRef<HTMLDivElement>(null)

  const nextId = useCallback((): number => {
    let id = 0
    setIdCtr(prev => { id = prev + 1; return prev + 1 })
    return id
  }, [])

  const addLines = useCallback((texts: string[], kind: TermLine["kind"] = "output") => {
    setLines(prev => [
      ...prev,
      ...texts.map(text => ({ id: 0, kind, text })),
    ].map((l, i, arr) => ({ ...l, id: arr.length - texts.length + i }))
    // Note: IDs don't need to be perfect unique here since we append
    )
    setIdCtr(prev => prev + texts.length)
  }, [])

  // Initialize MOTD on mount / server change
  useEffect(() => {
    const motd = isLive ? liveMotd(server) : mockMotd(server)
    const init = motd.split("\n").map((text, i) => ({ id: i, kind: "output" as const, text }))
    setLines(init)
    setIdCtr(init.length)
    setInput("")
    setHistory([])
    setHistIdx(-1)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [server.uid, isLive])

  // Auto-scroll
  useEffect(() => {
    scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth" })
  }, [lines])

  const prompt = `[admin@${server.identityName ?? server.name}] > `

  const submit = useCallback(async () => {
    if (executing) return
    const cmd = input.trim()

    setLines(prev => [...prev, { id: nextId(), kind: "prompt", text: prompt + input }])
    setInput("")
    setHistIdx(-1)

    if (!cmd) return

    setHistory(h => [cmd, ...h.filter(x => x !== cmd)].slice(0, 100))

    // local always-available commands
    const lower = cmd.toLowerCase()
    if (lower === "clear" || lower === "/clear") { setLines([]); return }
    if (lower === "quit"  || lower === "exit")   {
      setLines(prev => [...prev, { id: nextId(), kind: "info", text: "Connection closed." }])
      return
    }

    if (isLive && server.backendId !== null) {
      setExecuting(true)
      try {
        const res  = await fetch(`${backendUrl}/api/servers/${server.backendId}/exec`, {
          method:  "POST",
          headers: { "Content-Type": "application/json" },
          body:    JSON.stringify({ command: cmd }),
        })
        const data = await res.json() as { output?: string; error?: string }
        const text = data.output ?? data.error ?? "(empty response)"
        const kind: TermLine["kind"] = text.startsWith("error:") ? "error" : "output"
        text.split("\n").forEach(line =>
          setLines(prev => [...prev, { id: nextId(), kind, text: line }])
        )
      } catch (err) {
        setLines(prev => [...prev, { id: nextId(), kind: "error", text: `network error: ${String(err)}` }])
      } finally {
        setExecuting(false)
      }
    } else {
      // mock mode
      const result = mockResolve(cmd, server.name)
      if (result === "__CLEAR__") { setLines([]); return }
      if (result) {
        result.split("\n").forEach(line =>
          setLines(prev => [...prev, { id: nextId(), kind: "output", text: line }])
        )
      }
    }
  }, [executing, input, nextId, prompt, isLive, server, backendUrl])

  const handleKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === "Enter") {
      e.preventDefault(); void submit()
    } else if (e.key === "ArrowUp") {
      e.preventDefault()
      setHistIdx(i => {
        const next = Math.min(i + 1, history.length - 1)
        if (history[next] !== undefined) setInput(history[next])
        return next
      })
    } else if (e.key === "ArrowDown") {
      e.preventDefault()
      setHistIdx(i => {
        const next = i - 1
        if (next < 0) { setInput(""); return -1 }
        if (history[next] !== undefined) setInput(history[next])
        return next
      })
    } else if (e.key === "l" && e.ctrlKey) {
      e.preventDefault(); setLines([])
    }
  }

  const lineColor = (kind: TermLine["kind"]) => {
    if (kind === "error")  return "text-red-400"
    if (kind === "prompt") return "text-emerald-400"
    if (kind === "info")   return "text-sky-400"
    return "text-[#c9d1d9]"
  }

  return (
    <div
      className="flex flex-col bg-[#0d1117] rounded-lg border border-[#30363d] overflow-hidden font-mono text-xs h-full"
      onClick={() => inputRef.current?.focus()}
    >
      {/* title bar */}
      <div className="flex items-center gap-2 px-3 py-2 border-b border-[#30363d] bg-[#161b22] shrink-0">
        <CircleIcon className="size-3 text-red-500   fill-red-500" />
        <CircleIcon className="size-3 text-amber-400 fill-amber-400" />
        <CircleIcon className="size-3 text-emerald-500 fill-emerald-500" />
        <span className="mx-auto text-[#8b949e] text-[11px]">
          {server.name} — {isLive ? "RouterOS REST" : "SSH Terminal (mock)"}
        </span>
        {isLive && (
          <span className="inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] font-medium border border-emerald-500/30 bg-emerald-500/10 text-emerald-400">
            <span className="size-1 rounded-full bg-emerald-400" />LIVE
          </span>
        )}
        <button
          onClick={e => { e.stopPropagation(); setLines([]) }}
          className="text-[#8b949e] hover:text-white transition-colors ml-1"
          title="Очистить"
        >
          <TrashIcon className="size-3.5" />
        </button>
      </div>

      {/* output */}
      <div ref={scrollRef} className="flex-1 overflow-y-auto p-3 space-y-0.5 min-h-0">
        {lines.map((line, i) => (
          <div key={`${line.id}-${i}`} className={cn("whitespace-pre-wrap break-all leading-5", lineColor(line.kind))}>
            {line.text}
          </div>
        ))}

        {/* executing indicator */}
        {executing && (
          <div className="flex items-center gap-2 text-amber-400 opacity-80">
            <Loader2Icon className="size-3 animate-spin" />
            <span>executing…</span>
          </div>
        )}

        {/* input line */}
        {!executing && (
          <div className="flex items-center gap-0">
            <span className="text-emerald-400 select-none shrink-0">{prompt}</span>
            <input
              ref={inputRef}
              value={input}
              onChange={e => setInput(e.target.value)}
              onKeyDown={handleKey}
              className="terminal-input-active flex-1 bg-transparent outline-none text-[#c9d1d9] caret-emerald-400"
              autoComplete="off"
              autoCorrect="off"
              autoCapitalize="off"
              spellCheck={false}
            />
          </div>
        )}
      </div>
    </div>
  )
}

// ─── quick commands ───────────────────────────────────────────────────────────

const QUICK_CMDS = [
  { cmd: "system resource print",         label: "system resource print" },
  { cmd: "interface print",               label: "interface print" },
  { cmd: "ip address print",              label: "ip address print" },
  { cmd: "ip route print",               label: "ip route print" },
  { cmd: "ip firewall filter print",      label: "ip firewall filter print" },
  { cmd: "routing bgp session print",     label: "routing bgp session print" },
  { cmd: "routing ospf neighbor print",   label: "routing ospf neighbor print" },
  { cmd: "routing ospf area print",       label: "routing ospf area print" },
  { cmd: "routing bfd session print",     label: "routing bfd session print" },
  { cmd: "log print",                     label: "log print" },
]

// ─── page ─────────────────────────────────────────────────────────────────────

/** Convert mock server list to TermServer[] */
function mockServersToTermServers(): TermServer[] {
  return mockServers.map(s => ({
    uid:          s.id,
    backendId:    null,
    name:         s.name,
    host:         s.host,
    country:      s.country,
    status:       s.status as TermServer["status"],
    enabled:      s.enabled,
    rosVersion:   s.os ?? null,
    identityName: s.name,
  }))
}

/** Backend ServerRead shape (only fields we need) */
interface BackendServer {
  id:           number
  name:         string
  host:         string
  country:      string
  status:       "online" | "offline" | null
  enabled:      boolean
  os:           string | null
  identityName: string | null
}

export default function TerminalPage() {
  const { mode, backendUrl, backendStatus } = useDataSource()
  const isLive = mode === "live" && backendStatus === true

  // Server list state
  const [liveServers,  setLiveServers]  = useState<TermServer[]>([])
  const [serversLoading, setServersLoading] = useState(false)
  const [refreshKey,   setRefreshKey]   = useState(0)   // force terminal remount on reconnect

  // Load servers from backend when in live mode
  useEffect(() => {
    if (!isLive) { setLiveServers([]); return }
    let cancelled = false
    setServersLoading(true)
    fetch(`${backendUrl}/api/servers`)
      .then(r => r.json() as Promise<BackendServer[]>)
      .then(data => {
        if (cancelled) return
        setLiveServers(data.map(s => ({
          uid:          String(s.id),
          backendId:    s.id,
          name:         s.name || s.host,
          host:         s.host,
          country:      s.country || "",
          status:       s.status,
          enabled:      s.enabled,
          rosVersion:   s.os,
          identityName: s.identityName,
        })))
        setServersLoading(false)
      })
      .catch(() => { if (!cancelled) setServersLoading(false) })
    return () => { cancelled = true }
  }, [isLive, backendUrl, refreshKey])

  const termServers: TermServer[] = isLive ? liveServers : mockServersToTermServers()

  // Select first available server by default
  const [selectedUid, setSelectedUid] = useState<string>("")

  const defaultUid = useMemo(() => {
    const first = termServers.find(s => s.status !== "offline" && s.enabled)
               ?? termServers[0]
    return first?.uid ?? ""
  }, [termServers])

  useEffect(() => {
    if (!selectedUid && defaultUid) setSelectedUid(defaultUid)
  }, [defaultUid, selectedUid])

  // Reset selection when switching modes
  useEffect(() => { setSelectedUid("") }, [isLive])

  const selected = termServers.find(s => s.uid === selectedUid) ?? termServers[0]

  const termKey = `${selectedUid}-${refreshKey}-${isLive ? "live" : "mock"}`

  function injectCommand(cmd: string) {
    const el = document.querySelector<HTMLInputElement>(".terminal-input-active")
    if (!el) return
    const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set
    nativeSetter?.call(el, cmd)
    el.dispatchEvent(new Event("input", { bubbles: true }))
    el.focus()
  }

  return (
    <div className="flex flex-col h-full">
      <PageHeader
        crumbs={[{ label: "Инструменты" }, { label: "Терминал" }]}
        actions={
          <Button variant="outline" size="sm" onClick={() => {
            setRefreshKey(k => k + 1)
            if (isLive) setSelectedUid("")
          }}>
            <RefreshCwIcon className="size-4" />Переподключить
          </Button>
        }
      />

      <div className="flex-1 overflow-hidden p-6">
        <div className="grid grid-cols-[220px_1fr] gap-5 h-full">

          {/* ── sidebar ── */}
          <div className="flex flex-col gap-4 overflow-y-auto min-h-0">

            {/* server picker */}
            <div>
              <div className="flex items-center gap-2 mb-2">
                <p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">Узел</p>
                {isLive && serversLoading && (
                  <Loader2Icon className="size-3 animate-spin text-muted-foreground" />
                )}
                {isLive && !serversLoading && (
                  <span className="inline-flex items-center gap-1 text-[10px] font-medium rounded border border-emerald-500/30 bg-emerald-500/10 text-emerald-400 px-1.5 py-0.5">
                    <span className="size-1 rounded-full bg-emerald-400" />LIVE
                  </span>
                )}
              </div>

              {isLive && !serversLoading && liveServers.length === 0 && (
                <p className="text-xs text-muted-foreground px-2.5">
                  Нет доступных серверов
                </p>
              )}

              <div className="space-y-1">
                {termServers.map(s => {
                  const isOffline  = s.status === "offline"
                  const isSelected = s.uid === selectedUid
                  return (
                    <button
                      key={s.uid}
                      disabled={isOffline || !s.enabled}
                      onClick={() => { setSelectedUid(s.uid); setRefreshKey(k => k + 1) }}
                      className={cn(
                        "w-full text-left rounded-md px-2.5 py-2 text-xs transition-colors",
                        "flex items-center gap-2",
                        isSelected
                          ? "bg-primary text-primary-foreground"
                          : "hover:bg-muted",
                        (isOffline || !s.enabled) && "opacity-40 cursor-not-allowed",
                      )}
                    >
                      <span className={cn(
                        "inline-block size-1.5 rounded-full shrink-0",
                        s.status === "online"   ? "bg-emerald-500" :
                        s.status === "degraded" ? "bg-amber-400"   :
                        s.status === null       ? "bg-sky-400"     : "bg-red-500",
                      )} />
                      {s.country && <Flag code={s.country} />}
                      <span className="truncate font-mono">{s.name}</span>
                    </button>
                  )
                })}
              </div>
            </div>

            {/* quick commands */}
            <div>
              <p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">
                Быстрые команды
              </p>
              <div className="space-y-1">
                {QUICK_CMDS.map(({ cmd, label }) => (
                  <button
                    key={cmd}
                    className="w-full text-left rounded-md px-2.5 py-1.5 text-[11px] font-mono text-muted-foreground hover:bg-muted hover:text-foreground transition-colors truncate block"
                    onClick={() => injectCommand(cmd)}
                    title={cmd}
                  >
                    {label}
                  </button>
                ))}
              </div>
            </div>

            {/* hints */}
            <div className="mt-auto text-[10px] text-muted-foreground/50 space-y-0.5 px-0.5">
              <p>↑↓ — история команд</p>
              <p>Ctrl+L — очистить экран</p>
              {isLive
                ? <p className="text-sky-400/60">Команды выполняются на роутере</p>
                : <p>Режим: mock-данные</p>
              }
            </div>
          </div>

          {/* ── terminal ── */}
          {selected ? (
            <Terminal
              key={termKey}
              server={selected}
              isLive={isLive}
              backendUrl={backendUrl}
            />
          ) : (
            <div className="flex items-center justify-center bg-[#0d1117] rounded-lg border border-[#30363d] text-[#8b949e] text-sm font-mono">
              {serversLoading ? "Загрузка серверов…" : "Выберите сервер"}
            </div>
          )}
        </div>
      </div>
    </div>
  )
}
