"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 type { ServerStatus } from "@/lib/data"
import { useDataSource } from "@/lib/data-source"
import { requestJson } from "@/shared/api/http-client"
import { Badge } from "@/components/reui/badge"
import {
  Frame,
  FrameFooter,
  FrameHeader,
  FramePanel,
  FrameTitle,
} from "@/components/reui/frame"
import { ScrollArea } from "@/components/ui/scroll-area"
import { ServerRailLayout, ServerRailMobileButton } from "@/components/server-rail-layout"
import {
  type ServerTileItem,
} from "@/components/server-tile-rail"
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
  }, [])

  // 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 data = await requestJson<{ output?: string; error?: string }>(
          backendUrl,
          `/api/servers/${server.backendId}/exec`,
          {
            method: "POST",
            body: JSON.stringify({ command: cmd }),
          },
        )
        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, prefsHydrated } = useDataSource()
  const isLive = mode === "live"

  // 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 || !prefsHydrated) return
    let cancelled = false
    queueMicrotask(() => {
      if (cancelled) return
      setServersLoading(true)
      void requestJson<BackendServer[]>(backendUrl, "/api/servers")
        .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, prefsHydrated])

  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(() => {
    queueMicrotask(() => {
      if (!selectedUid && defaultUid) setSelectedUid(defaultUid)
    })
  }, [defaultUid, selectedUid])

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

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

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

  const railItems = useMemo<ServerTileItem[]>(() => {
    return termServers.map((s) => ({
      id: s.uid,
      name: s.name,
      host: s.host,
      country: s.country || undefined,
      status: (s.status ?? undefined) as ServerStatus | undefined,
      enabled: s.enabled,
      selectable: s.enabled && s.status !== "offline",
      title: [s.name, s.host].filter(Boolean).join(" · "),
    }))
  }, [termServers])

  const handleSelectServer = useCallback((id: string) => {
    setSelectedUid(id)
    setRefreshKey((k) => k + 1)
  }, [])

  const railHeaderRight = isLive && !serversLoading
    ? <Badge variant="success-light" size="xs">LIVE</Badge>
    : undefined

  function QuickCmds() {
    return (
      <Frame dense spacing="sm" className="min-h-0 shrink-0">
        <FramePanel className="flex max-h-56 flex-col gap-0 p-0">
          <FrameHeader className="border-b px-3 py-2">
            <FrameTitle className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
              Быстрые команды
            </FrameTitle>
          </FrameHeader>
          <ScrollArea className="min-h-0 flex-1">
            <div className="flex flex-col gap-0.5 p-1.5">
              {QUICK_CMDS.map(({ cmd, label }) => (
                <button
                  key={cmd}
                  type="button"
                  className="truncate rounded-md px-2 py-1.5 text-left font-mono text-[11px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
                  onClick={() => injectCommand(cmd)}
                  title={cmd}
                >
                  {label}
                </button>
              ))}
            </div>
          </ScrollArea>
          <FrameFooter className="border-t text-[10px] text-muted-foreground/70">
            <p>↑↓ — история команд</p>
            <p>Ctrl+L — очистить экран</p>
            {isLive
              ? <p className="text-info">Команды выполняются на роутере</p>
              : <p>Режим: mock-данные</p>}
          </FrameFooter>
        </FramePanel>
      </Frame>
    )
  }

  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 (
    <ServerRailLayout
      items={railItems}
      selectedId={selected?.uid ?? selectedUid}
      onSelect={handleSelectServer}
      showAll={false}
      showCount={false}
      showType={false}
      headerRight={railHeaderRight}
      loading={serversLoading}
      extra={<QuickCmds />}
      contentClassName="overflow-hidden p-3 md:p-4"
      header={
        <PageHeader
          crumbs={[{ label: "Инструменты" }, { label: "Терминал" }]}
          actions={
            <>
              <ServerRailMobileButton />
              <Button
                variant="outline"
                size="sm"
                onClick={() => {
                  setRefreshKey((k) => k + 1)
                  if (isLive) setSelectedUid("")
                }}
              >
                <RefreshCwIcon className="size-4" />
                Переподключить
              </Button>
            </>
          }
        />
      }
    >
          {selected ? (
            <Terminal
              key={termKey}
              server={selected}
              isLive={isLive}
              backendUrl={backendUrl}
            />
          ) : (
            <div className="flex h-full items-center justify-center rounded-lg border border-[#30363d] bg-[#0d1117] font-mono text-sm text-[#8b949e]">
              {serversLoading ? "Загрузка серверов…" : "Выберите сервер"}
            </div>
          )}
    </ServerRailLayout>
  )
}
