import { useEffect, useMemo, useRef, useState } from 'react'
import { Navigate, NavLink, Outlet, useLocation, useNavigate } from 'react-router-dom'
import { LogOut, Menu, Search, UserRound } from 'lucide-react'
import {
  avatarColors, Badge, CommandPalette, initials, ThemeSwitcher, type PaletteItem,
} from '@sims/ui'
import { clearSession, displayName, getClients, getEmailStatus, getReminders, hasSession, role } from './api'
import { NAV_GROUPS } from './nav'

const APP_VERSION = 'v0.1.0'

/** App shell: grouped sidebar + top bar + Ctrl+K palette + Gmail-health banner. */
export function Layout() {
  const nav = useNavigate()
  const location = useLocation()
  const [gmail, setGmail] = useState<'ok' | 'down' | 'unknown'>('unknown')
  // Sidebar collapse (D18 WS-E): icon rail on demand, remembered across reloads.
  const [collapsed, setCollapsed] = useState(() => localStorage.getItem('hq.sideCollapsed') === '1')
  const toggleCollapsed = () => setCollapsed((v) => {
    localStorage.setItem('hq.sideCollapsed', v ? '0' : '1')
    return !v
  })
  const [queueCounts, setQueueCounts] = useState({ queued: 0, failed: 0 })
  const [healthy, setHealthy] = useState<boolean | undefined>()
  const [menuOpen, setMenuOpen] = useState(false)
  const [userOpen, setUserOpen] = useState(false)
  const userRef = useRef<HTMLSpanElement>(null)
  const [paletteOpen, setPaletteOpen] = useState(false)
  const [clientHits, setClientHits] = useState<PaletteItem[]>([])
  const queryTimer = useRef<number | undefined>(undefined)
  const querySeq = useRef(0)

  useEffect(() => {
    if (!hasSession()) return
    getEmailStatus()
      .then((s) => setGmail(!s.connected || s.dead ? 'down' : 'ok'))
      .catch(() => { /* 401 already redirected; other failures keep it unknown */ })
    fetch('/api/health').then((r) => setHealthy(r.ok)).catch(() => setHealthy(false))
  }, [])

  // Reminder-queue badge (Dashboard nav item) — refreshed on every route change.
  // Uses the honest scoped `total` (never rows.length): staff badge counts match
  // their own queue, and pagination cannot under-count.
  useEffect(() => {
    if (!hasSession()) return
    Promise.all([getReminders({ status: 'queued' }), getReminders({ status: 'failed' })])
      .then(([q, f]) => setQueueCounts({ queued: q.total, failed: f.total }))
      .catch(() => { /* badge is best-effort */ })
  }, [location.pathname])

  // Close the mobile drawer and the user menu when the route changes.
  useEffect(() => { setMenuOpen(false); setUserOpen(false) }, [location.pathname])

  // User menu dismissal: outside click or Escape.
  useEffect(() => {
    if (!userOpen) return
    const onDown = (e: MouseEvent) => {
      if (userRef.current !== null && !userRef.current.contains(e.target as Node)) setUserOpen(false)
    }
    const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setUserOpen(false) }
    document.addEventListener('mousedown', onDown)
    document.addEventListener('keydown', onKey)
    return () => { document.removeEventListener('mousedown', onDown); document.removeEventListener('keydown', onKey) }
  }, [userOpen])

  // Cancel any pending palette query on unmount.
  useEffect(() => () => window.clearTimeout(queryTimer.current), [])

  // Ctrl+K / Cmd+K opens the palette.
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
        if (e.repeat) return
        e.preventDefault()
        setPaletteOpen((v) => !v)
      }
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  }, [])

  // Tab title tracks the active nav section (spec §3); record routes fall back to
  // their section title since they aren't separate nav items. Exact match wins
  // before prefix match so /documents/new titles as 'New Document', not 'Documents'.
  useEffect(() => {
    const items = NAV_GROUPS.flatMap((g) => g.items)
    const item = items.find((i) => location.pathname === i.to)
      ?? items.find((i) => i.to !== '/' && location.pathname.startsWith(i.to))
    document.title = item !== undefined ? `${item.label} · SiMS HQ` : 'SiMS HQ'
  }, [location.pathname])

  const staticItems = useMemo<PaletteItem[]>(() => {
    const isOwner = role() === 'owner'
    const navItems = NAV_GROUPS.flatMap((g) => g.items)
      .filter((i) => i.ownerOnly !== true || isOwner)
      .map((i) => ({ id: `nav:${i.to}`, label: i.label, group: 'Go to' }))
    return [
      ...navItems,
      { id: 'act:/documents/new', label: 'New document', group: 'Actions', hint: 'quotation · proforma · invoice' },
      { id: 'act:/clients?new=1', label: 'New client', group: 'Actions' },
    ]
  }, [])

  const onPaletteQuery = (q: string) => {
    window.clearTimeout(queryTimer.current)
    if (q.trim().length < 2) { querySeq.current++; setClientHits([]); return }
    const mySeq = ++querySeq.current
    queryTimer.current = window.setTimeout(() => {
      getClients(q)
        .then((cs) => {
          if (querySeq.current !== mySeq) return
          setClientHits(cs.slice(0, 8).map((c) => ({
            id: `client:${c.id}`, label: `${c.code} · ${c.name}`, group: 'Clients', hint: c.status,
          })))
        })
        .catch(() => {
          if (querySeq.current !== mySeq) return
          setClientHits([{ id: 'err:clients', label: 'Client search failed — is the server up?', group: 'Clients' }])
        })
    }, 200)
  }

  const onPaletteSelect = (item: PaletteItem) => {
    if (item.id.startsWith('err:')) return
    if (item.id.startsWith('nav:')) nav(item.id.slice(4))
    else if (item.id.startsWith('act:')) nav(item.id.slice(4))
    else if (item.id.startsWith('client:')) nav(`/clients/${item.id.slice(7)}`)
  }

  if (!hasSession()) return <Navigate to="/login" replace />

  const isOwner = role() === 'owner'
  const groups = NAV_GROUPS
    .map((g) => ({ ...g, items: g.items.filter((i) => i.ownerOnly !== true || isOwner) }))
    .filter((g) => g.items.length > 0)
  const name = displayName()
  const av = avatarColors(name)
  const badgeCount = queueCounts.queued + queueCounts.failed
  const badgeTitle = `${queueCounts.queued} queued${queueCounts.failed > 0 ? `, ${queueCounts.failed} failed` : ''}`

  return (
    <div className={`hq-shell${collapsed ? ' side-collapsed' : ''}`}>
      {menuOpen && <div className="hq-scrim" onClick={() => setMenuOpen(false)} />}
      <nav className={`hq-side${menuOpen ? ' open' : ''}`}>
        <div className="hq-brand">
          SiMS HQ
          <small>Ops console</small>
        </div>
        {groups.map((g) => (
          <div key={g.label} className="hq-group">
            <div className="hq-eyebrow">{g.label}</div>
            {g.items.map((n) => (
              <NavLink key={n.to} to={n.to} title={n.label} className={({ isActive }) => (isActive ? 'active' : '')} end={n.to === '/' || n.end === true}>
                <n.icon size={15} strokeWidth={1.8} aria-hidden />
                <span className="lbl">{n.label}</span>
                {n.to === '/' && badgeCount > 0 && (
                  <span
                    className={`hq-count${queueCounts.failed > 0 ? ' err' : ''}`}
                    title={badgeTitle}
                    aria-label={badgeTitle}
                  >{badgeCount}</span>
                )}
              </NavLink>
            ))}
          </div>
        ))}
        <div className="hq-side-foot">
          <span className="lbl">{APP_VERSION}</span>
          {healthy !== undefined && (
            <span className={`hq-dot${healthy ? ' ok' : ' err'}`} title={healthy ? 'Server healthy' : 'Server unreachable'} />
          )}
          <button
            type="button" className="hq-collapse" onClick={toggleCollapsed}
            title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
            aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
          >
            {collapsed ? '›' : '‹'}
          </button>
        </div>
      </nav>
      <div className="hq-main">
        {gmail === 'down' && (
          <div className="hq-banner">
            Gmail disconnected — sends are queued to manual. Run gmail-connect on the server.
          </div>
        )}
        <header className="hq-top">
          <button type="button" className="wf hq-burger" aria-label="Menu" aria-expanded={menuOpen} onClick={() => setMenuOpen((v) => !v)}>
            <Menu size={16} />
          </button>
          <button type="button" className="hq-search" onClick={() => setPaletteOpen(true)}>
            <Search size={14} aria-hidden />
            <span>Search clients, pages…</span>
            <kbd>Ctrl K</kbd>
          </button>
          <span style={{ flex: 1 }} />
          {gmail !== 'unknown' && (
            <span className={`hq-pill${gmail === 'ok' ? ' ok' : ' warn'}`}>
              <span className={`hq-dot${gmail === 'ok' ? ' ok' : ' warn'}`} />
              {gmail === 'ok' ? 'Gmail' : 'Gmail disconnected'}
            </span>
          )}
          <ThemeSwitcher />
          {/* D18 WS-E: the user chip opens a small menu — Profile / Logout. */}
          <span
            className="hq-user-wrap"
            ref={userRef}
            // Close when keyboard focus leaves the chip+menu — outside-close alone
            // only fires on mousedown, so a Tab-away would leave the menu hanging open.
            onBlur={(e) => {
              if (!e.currentTarget.contains(e.relatedTarget as Node | null)) setUserOpen(false)
            }}
          >
            <button
              type="button" className="hq-user" title="Account"
              aria-haspopup="menu" aria-expanded={userOpen}
              onClick={() => setUserOpen((v) => !v)}
            >
              <span className="hq-user-av" style={{ background: av.bg, color: av.fg }}>{initials(name)}</span>
              <span className="hq-user-name">{name}</span>
              <Badge>{role()}</Badge>
            </button>
            {userOpen && (
              <div className="hq-user-menu" role="menu">
                <button type="button" role="menuitem" className="hq-user-item" onClick={() => { setUserOpen(false); nav('/profile') }}>
                  <UserRound size={14} aria-hidden /> Profile
                </button>
                <button type="button" role="menuitem" className="hq-user-item" onClick={() => { setUserOpen(false); clearSession(); nav('/login') }}>
                  <LogOut size={14} aria-hidden /> Logout
                </button>
              </div>
            )}
          </span>
        </header>
        <div className="hq-content">
          <Outlet />
        </div>
      </div>
      <CommandPalette
        open={paletteOpen}
        onClose={() => setPaletteOpen(false)}
        items={[...clientHits, ...staticItems]}
        onSelect={onPaletteSelect}
        onQuery={onPaletteQuery}
        placeholder="Search clients, pages, actions…"
      />
    </div>
  )
}
