import { useEffect, useRef, useState } from 'react'
import { Terminal as XTerm } from '@xterm/xterm'
import { FitAddon } from '@xterm/addon-fit'
import '@xterm/xterm/css/xterm.css'
import { Settings2, Plus, X, Columns2, Grid2x2, SquareSlash } from 'lucide-react'
import { api, getToken, type Integration } from '../lib/api'

const GOLD = '#C8A434'
const TEXT_SECONDARY = '#7A7D85'

interface TermTheme {
  name: string
  background: string
  foreground: string
  cursor: string
}

const TERM_THEMES: TermTheme[] = [
  { name: 'ArchNest Dark', background: '#15161A', foreground: '#E8E6E0', cursor: GOLD },
  { name: 'Matrix', background: '#000000', foreground: '#33FF66', cursor: '#33FF66' },
  { name: 'Solarized', background: '#002B36', foreground: '#93A1A1', cursor: '#CB4B16' },
  { name: 'Midnight Blue', background: '#0B1021', foreground: '#C7D3F2', cursor: '#5FA8FF' },
]

const FONT_SIZES = [11, 12, 13, 14, 15, 16]
const FONT_FAMILIES = [
  { name: 'Monospace', value: 'ui-monospace, SFMono-Regular, Menlo, monospace' },
  { name: 'Fira Code', value: '"Fira Code", ui-monospace, monospace' },
  { name: 'JetBrains Mono', value: '"JetBrains Mono", ui-monospace, monospace' },
]

interface TerminalPrefs {
  themeName: string
  fontSize: number
  fontFamily: string
}

const PREFS_KEY = 'archnest-terminal-prefs'

function loadPrefs(): TerminalPrefs {
  try {
    const raw = localStorage.getItem(PREFS_KEY)
    if (raw) return { ...defaultPrefs(), ...JSON.parse(raw) }
  } catch {
    /* ignore malformed local storage */
  }
  return defaultPrefs()
}

function defaultPrefs(): TerminalPrefs {
  return { themeName: TERM_THEMES[0].name, fontSize: 13, fontFamily: FONT_FAMILIES[0].value }
}

function savePrefs(prefs: TerminalPrefs) {
  localStorage.setItem(PREFS_KEY, JSON.stringify(prefs))
}

interface PaneState {
  id: number
  hostId: number | null
}

interface TabState {
  id: number
  name: string
  panes: PaneState[]
}

let nextId = 1
const genId = () => nextId++

function newTab(): TabState {
  return { id: genId(), name: 'Terminal', panes: [{ id: genId(), hostId: null }] }
}

export default function Terminal() {
  const [hosts, setHosts] = useState<Integration[]>([])
  const [tabs, setTabs] = useState<TabState[]>(() => [newTab()])
  const [activeTabId, setActiveTabId] = useState(() => tabs[0].id)
  const [activePaneId, setActivePaneId] = useState<number | null>(null)
  const [prefs, setPrefs] = useState<TerminalPrefs>(loadPrefs)
  const [showPrefs, setShowPrefs] = useState(false)

  useEffect(() => {
    api.listIntegrations().then(({ integrations }) => {
      setHosts(integrations.filter((i) => i.type === 'ssh'))
    })
  }, [])

  useEffect(() => savePrefs(prefs), [prefs])

  const activeTab = tabs.find((t) => t.id === activeTabId) ?? tabs[0]

  function addTab() {
    const tab = newTab()
    setTabs((prev) => [...prev, tab])
    setActiveTabId(tab.id)
  }

  function closeTab(id: number) {
    setTabs((prev) => {
      const next = prev.filter((t) => t.id !== id)
      if (next.length === 0) {
        const t = newTab()
        setActiveTabId(t.id)
        return [t]
      }
      if (id === activeTabId) setActiveTabId(next[0].id)
      return next
    })
  }

  function updateActiveTab(fn: (tab: TabState) => TabState) {
    setTabs((prev) => prev.map((t) => (t.id === activeTabId ? fn(t) : t)))
  }

  function setPaneCount(count: number) {
    updateActiveTab((tab) => {
      const panes = [...tab.panes]
      while (panes.length < count) panes.push({ id: genId(), hostId: null })
      while (panes.length > count) panes.pop()
      return { ...tab, panes }
    })
  }

  function setPaneHost(paneId: number, hostId: number) {
    updateActiveTab((tab) => ({
      ...tab,
      panes: tab.panes.map((p) => (p.id === paneId ? { ...p, hostId } : p)),
    }))
    setActivePaneId(paneId)
  }

  const paneGridClass =
    activeTab.panes.length === 1
      ? 'grid-cols-1 grid-rows-1'
      : activeTab.panes.length === 2
        ? 'grid-cols-2 grid-rows-1'
        : 'grid-cols-2 grid-rows-2'

  return (
    <div className="flex h-full gap-4">
      <div className="w-56 shrink-0 overflow-y-auto rounded-lg border border-white/10 bg-white/5 p-3">
        <p className="mb-2 text-xs font-medium uppercase tracking-wide" style={{ color: TEXT_SECONDARY }}>
          SSH Hosts
        </p>
        {hosts.length === 0 && (
          <p className="text-xs" style={{ color: TEXT_SECONDARY }}>
            No SSH integrations configured. Add one in Settings → Integrations.
          </p>
        )}
        <div className="flex flex-col gap-1">
          {hosts.map((h) => (
            <button
              key={h.id}
              onClick={() => activePaneId !== null && setPaneHost(activePaneId, h.id)}
              className="rounded-md px-2 py-1.5 text-left text-sm transition-colors"
              style={{
                background: activeTab.panes.find((p) => p.id === activePaneId)?.hostId === h.id ? 'rgba(200,164,52,0.15)' : 'transparent',
                color: activeTab.panes.find((p) => p.id === activePaneId)?.hostId === h.id ? GOLD : '#E8E6E0',
              }}
            >
              {h.name}
            </button>
          ))}
        </div>
        <p className="mt-3 text-[11px]" style={{ color: TEXT_SECONDARY }}>
          Click a pane, then a host to connect it.
        </p>
      </div>

      <div className="flex min-w-0 flex-1 flex-col gap-2">
        <div className="flex items-center gap-2">
          <div className="flex flex-1 items-center gap-1 overflow-x-auto">
            {tabs.map((tab) => (
              <div
                key={tab.id}
                onClick={() => setActiveTabId(tab.id)}
                className="flex shrink-0 cursor-pointer items-center gap-2 rounded-md px-3 py-1.5 text-xs"
                style={{
                  background: tab.id === activeTabId ? 'rgba(200,164,52,0.15)' : 'rgba(255,255,255,0.04)',
                  color: tab.id === activeTabId ? GOLD : '#E8E6E0',
                }}
              >
                {tab.name}
                <X
                  size={12}
                  onClick={(e) => {
                    e.stopPropagation()
                    closeTab(tab.id)
                  }}
                />
              </div>
            ))}
            <button onClick={addTab} className="shrink-0 rounded-md p-1.5" style={{ color: TEXT_SECONDARY }} title="New tab">
              <Plus size={14} />
            </button>
          </div>

          <div className="flex items-center gap-1">
            <button onClick={() => setPaneCount(1)} className="rounded-md p-1.5" style={{ color: activeTab.panes.length === 1 ? GOLD : TEXT_SECONDARY }} title="Single pane">
              <SquareSlash size={14} />
            </button>
            <button onClick={() => setPaneCount(2)} className="rounded-md p-1.5" style={{ color: activeTab.panes.length === 2 ? GOLD : TEXT_SECONDARY }} title="Split 2">
              <Columns2 size={14} />
            </button>
            <button onClick={() => setPaneCount(4)} className="rounded-md p-1.5" style={{ color: activeTab.panes.length === 4 ? GOLD : TEXT_SECONDARY }} title="Split 4">
              <Grid2x2 size={14} />
            </button>
            <button onClick={() => setShowPrefs((v) => !v)} className="rounded-md p-1.5" style={{ color: showPrefs ? GOLD : TEXT_SECONDARY }} title="Terminal preferences">
              <Settings2 size={14} />
            </button>
          </div>
        </div>

        {showPrefs && (
          <div className="flex items-center gap-4 rounded-lg border border-white/10 bg-white/5 px-3 py-2 text-xs" style={{ color: '#E8E6E0' }}>
            <label className="flex items-center gap-2">
              Theme
              <select
                value={prefs.themeName}
                onChange={(e) => setPrefs((p) => ({ ...p, themeName: e.target.value }))}
                className="rounded-md border border-white/10 bg-transparent px-2 py-1"
              >
                {TERM_THEMES.map((t) => (
                  <option key={t.name} value={t.name}>
                    {t.name}
                  </option>
                ))}
              </select>
            </label>
            <label className="flex items-center gap-2">
              Font Size
              <select
                value={prefs.fontSize}
                onChange={(e) => setPrefs((p) => ({ ...p, fontSize: Number(e.target.value) }))}
                className="rounded-md border border-white/10 bg-transparent px-2 py-1"
              >
                {FONT_SIZES.map((s) => (
                  <option key={s} value={s}>
                    {s}px
                  </option>
                ))}
              </select>
            </label>
            <label className="flex items-center gap-2">
              Font
              <select
                value={prefs.fontFamily}
                onChange={(e) => setPrefs((p) => ({ ...p, fontFamily: e.target.value }))}
                className="rounded-md border border-white/10 bg-transparent px-2 py-1"
              >
                {FONT_FAMILIES.map((f) => (
                  <option key={f.name} value={f.value}>
                    {f.name}
                  </option>
                ))}
              </select>
            </label>
          </div>
        )}

        <div className={`grid min-h-0 flex-1 gap-2 ${paneGridClass}`}>
          {activeTab.panes.map((pane) => (
            <TerminalPane
              key={pane.id}
              hostId={pane.hostId}
              hosts={hosts}
              prefs={prefs}
              active={pane.id === activePaneId}
              onFocus={() => setActivePaneId(pane.id)}
            />
          ))}
        </div>
      </div>
    </div>
  )
}

function TerminalPane({
  hostId,
  hosts,
  prefs,
  active,
  onFocus,
}: {
  hostId: number | null
  hosts: Integration[]
  prefs: TerminalPrefs
  active: boolean
  onFocus: () => void
}) {
  const [connected, setConnected] = useState(false)
  const containerRef = useRef<HTMLDivElement>(null)
  const termRef = useRef<XTerm | null>(null)
  const fitRef = useRef<FitAddon | null>(null)
  const wsRef = useRef<WebSocket | null>(null)
  const lastHostIdRef = useRef<number | null>(null)

  useEffect(() => {
    if (!containerRef.current) return
    const theme = TERM_THEMES.find((t) => t.name === prefs.themeName) ?? TERM_THEMES[0]
    const term = new XTerm({
      cursorBlink: true,
      fontSize: prefs.fontSize,
      fontFamily: prefs.fontFamily,
      theme: { background: theme.background, foreground: theme.foreground, cursor: theme.cursor },
    })
    const fit = new FitAddon()
    term.loadAddon(fit)
    term.open(containerRef.current)
    fit.fit()
    termRef.current = term
    fitRef.current = fit

    const onResize = () => fit.fit()
    window.addEventListener('resize', onResize)
    return () => {
      window.removeEventListener('resize', onResize)
      term.dispose()
      wsRef.current?.close()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [prefs.themeName, prefs.fontSize, prefs.fontFamily])

  useEffect(() => {
    fitRef.current?.fit()
  })

  useEffect(() => {
    if (hostId === null || hostId === lastHostIdRef.current) return
    lastHostIdRef.current = hostId
    connect(hostId)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [hostId])

  function connect(id: number) {
    wsRef.current?.close()
    setConnected(false)
    const term = termRef.current
    if (!term) return
    term.reset()
    term.writeln('Connecting…')

    const token = getToken()
    const proto = window.location.protocol === 'https:' ? 'wss' : 'ws'
    const ws = new WebSocket(`${proto}://${window.location.host}/api/terminal?token=${encodeURIComponent(token ?? '')}`)
    wsRef.current = ws

    ws.onopen = () => {
      ws.send(JSON.stringify({ type: 'connect', integrationId: id, cols: term.cols, rows: term.rows }))
    }
    ws.onmessage = (event) => {
      const msg = JSON.parse(event.data)
      if (msg.type === 'connected') {
        setConnected(true)
        term.reset()
      } else if (msg.type === 'data') {
        term.write(msg.data)
      } else if (msg.type === 'error') {
        term.writeln(`\r\n\x1b[31mError: ${msg.message}\x1b[0m`)
        setConnected(false)
      } else if (msg.type === 'closed') {
        term.writeln('\r\n\x1b[33mConnection closed.\x1b[0m')
        setConnected(false)
      }
    }
    ws.onclose = () => setConnected(false)

    term.onData((data) => {
      if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'input', data }))
    })
    term.onResize(({ cols, rows }) => {
      if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'resize', cols, rows }))
    })
  }

  const host = hosts.find((h) => h.id === hostId)

  return (
    <div
      onClick={onFocus}
      className="flex min-w-0 min-h-0 flex-col rounded-lg p-2"
      style={{
        backgroundColor: '#15161A',
        border: active ? `1px solid ${GOLD}` : '1px solid rgba(255,255,255,0.1)',
      }}
    >
      <div className="mb-2 flex items-center gap-2 px-1 text-xs" style={{ color: TEXT_SECONDARY }}>
        <span className="inline-block h-2 w-2 rounded-full" style={{ background: connected ? '#2ECC71' : '#7A7D85' }} />
        {host ? (connected ? `Connected — ${host.name}` : `Disconnected — ${host.name}`) : 'Select a host to connect'}
      </div>
      <div ref={containerRef} className="min-h-0 flex-1" />
    </div>
  )
}
