import { ReactNode, memo, useEffect, useMemo, useRef, useState } from 'react';
import { NavLink, useLocation } from 'react-router-dom';
import AmbientBackground from '../Ambient/AmbientBackground';
import SystemStatusBar from '../Visual/SystemStatusBar';
import { useWebSocket } from '../../hooks/useWebSocket';
import { useIsMobileLayout } from '../../hooks/useMediaQuery';
import MatrixRain from './MatrixRain';
import afLogo from '../../assets/af-logo.png';
import CursorFire from '../Visual/CursorFire';
import SacredGeometryLayer from '../Visual/sacredGeometry/SacredGeometryLayer';
import { SacredMotif } from '../Visual/sacredGeometry/motifs';
import SetupBanner from '../SetupBanner';
import { getSetupStatus } from '../../help/setupStatus';
import { resolvePageWeather } from '../../help/pageWeather';
import { mergeScoutBiomeWeather, type ScoutConstellationSnapshot } from '../../help/scoutBiomeWeather';
import { isDashboardRoute } from '../../help/routeEffects';
import { api } from '../../api/client';
import { usePresence } from '../../context/PresenceContext';
import { useVisualEffects } from '../../context/VisualEffectsContext';
import ComradeAvatar from '../Presence/ComradeAvatar';
import { formatAppVersion } from '../../help/appVersion';
import type { ServerConfig, ServerInfo } from '../../types';
import '../Presence/Presence.css';
import './Layout.css';
import './MobileNav.css';
import '../../styles/operatorDeck.css';

interface LayoutProps {
  children: ReactNode;
}

function operatorDeckId(pathname: string): string {
  const path = pathname.split('?')[0].replace(/\/$/, '') || '/';
  if (path.startsWith('/mission-deck')) return 'mission-deck';
  if (path.startsWith('/forge') || path.startsWith('/builder')) return 'forge';
  if (path.startsWith('/crucible')) return 'crucible';
  if (path.startsWith('/emberwake') || path.startsWith('/spread')) return 'emberwake';
  if (path.startsWith('/deploy-recon') || path.startsWith('/browser-spread')) return 'deploy-recon';
  if (path.startsWith('/builds')) return 'builds';
  if (path.startsWith('/settings')) return 'settings';
  if (path.startsWith('/pathtracer')) return 'pathtracer';
  if (path.startsWith('/lotl-timeline') || path.startsWith('/onion')) return 'lotl-timeline';
  if (path.startsWith('/roi')) return 'roi';
  if (path.startsWith('/activity')) return 'activity';
  if (path.startsWith('/seer')) return 'seer';
  return 'dashboard';
}

type NavItem = { readonly to: string; readonly label: string; readonly icon: string; readonly glow?: boolean };

const NAV_BASE: readonly NavItem[] = [
  { to: '/dashboard', label: 'Command Deck', icon: 'deck' },
  { to: '/crucible', label: 'Crucible', icon: 'crucible' },
  { to: '/deploy-recon', label: 'Website Scanner', icon: 'recon' },
  { to: '/activity', label: 'Activity Feed', icon: 'activity' },
  { to: '/roi', label: 'ROI Intelligence', icon: 'roi' },
  { to: '/lotl-timeline', label: 'Onion', icon: 'onion' },
  { to: '/pathtracer', label: 'Path Tracer', icon: 'trace' },
  { to: '/forge', label: 'Forge', icon: 'forge' },
  { to: '/mission-deck', label: 'Mission Deck', icon: 'mission', glow: true },
  { to: '/builds', label: 'Builds', icon: 'builds' },
  { to: '/emberwake', label: 'Emberwake', icon: 'ember' },
  { to: '/settings', label: 'Calibrate', icon: 'gear' },
];

/** Exported for nav regression tests (route + label discoverability). */
export const operatorNavItems = NAV_BASE;

const SEER_NAV: NavItem = { to: '/seer', label: 'Seer', icon: 'seer' };

function buildNav(aiControlEnabled: boolean): NavItem[] {
  if (!aiControlEnabled) {
    return [...NAV_BASE];
  }
  const items: NavItem[] = [...NAV_BASE];
  const calibrateIdx = items.findIndex((i) => i.to === '/settings');
  items.splice(calibrateIdx, 0, SEER_NAV);
  return items;
}

const NAV = NAV_BASE;

const DOCS_HREF = '/docs/';

/** Primary tabs on mobile bottom bar — Deck, Crucible, Activity, ROI, Onion */
const MOBILE_PRIMARY = NAV.slice(0, 5);
/** Path Tracer, Forge, Mission Deck, Builds, Emberwake, Calibrate — "More" sheet */
const MOBILE_MORE = NAV.slice(5);

function NavIcon({ type }: { type: string }) {
  switch (type) {
    case 'deck':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <path d="M4 19V5h16v14M4 9h16M8 5v4M16 5v4" />
          <circle cx="12" cy="14" r="2" />
        </svg>
      );
    case 'roi':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <path d="M3 17l4-6 4 3 4-7 4 4" />
          <path d="M3 20h18" strokeOpacity="0.4" />
          <circle cx="19" cy="11" r="1.5" fill="currentColor" strokeWidth="0" />
        </svg>
      );
    case 'activity':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <polyline points="2,12 6,12 8,5 10,19 12,9 14,15 16,12 22,12" />
        </svg>
      );
    case 'fleet':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <rect x="2" y="6" width="20" height="12" rx="1" />
          <path d="M6 10h4M14 10h4M6 14h2M16 14h2" />
        </svg>
      );
    case 'forge':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <path d="M14 4l6 6-8 8H6v-6l8-8z" />
          <path d="M8 16l-2 4 4-2" />
        </svg>
      );
    case 'mission':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <path d="M13 2L3 14h8l-1 8 10-12h-8l1-8z" />
        </svg>
      );
    case 'builds':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <rect x="3" y="14" width="18" height="5" rx="1" />
          <rect x="3" y="8" width="18" height="5" rx="1" />
          <rect x="3" y="2" width="18" height="5" rx="1" />
          <path d="M7 4.5h10M7 10.5h10M7 16.5h10" strokeOpacity="0.35" />
        </svg>
      );
    case 'crucible':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <path d="M8 3h8l1 5H7L8 3z" />
          <path d="M7 8c0 5 2 8 5 10c3-2 5-5 5-10" />
          <path d="M4 21h16" />
          <path d="M10 12l1.5 2L14 11" />
        </svg>
      );
    case 'onion':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <circle cx="12" cy="13" r="6" />
          <circle cx="12" cy="13" r="3" strokeOpacity="0.45" />
          <path d="M12 4v3M12 19v3M4 13h3M17 13h3" strokeOpacity="0.5" />
        </svg>
      );
    case 'ember':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <path d="M12 3c2 4 4 5 4 9a4 4 0 01-8 0c0-4 2-5 4-9z" />
          <path d="M8 21h8" />
        </svg>
      );
    case 'recon':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <circle cx="11" cy="11" r="7" />
          <path d="M20 20l-3.5-3.5" />
          <path d="M8 11h6M11 8v6" strokeOpacity="0.45" />
        </svg>
      );
    case 'trace':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <circle cx="4"  cy="12" r="2" />
          <circle cx="12" cy="6"  r="2" />
          <circle cx="20" cy="12" r="2" />
          <path d="M6 12h4l2-6 2 6h4" />
          <path d="M12 8v4" />
        </svg>
      );
    case 'seer':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <ellipse cx="12" cy="12" rx="9" ry="5" />
          <circle cx="12" cy="12" r="2.5" fill="currentColor" strokeWidth="0" />
          <path d="M4 12c2-3 5-4.5 8-4.5s6 1.5 8 4.5" strokeOpacity="0.45" />
        </svg>
      );
    case 'docs':
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <path d="M4 19.5A2.5 2.5 0 016.5 17H20" />
          <path d="M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z" />
          <path d="M8 7h8M8 11h6" strokeOpacity="0.55" />
        </svg>
      );
    default:
      return (
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
          <circle cx="12" cy="12" r="3" />
          <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" />
        </svg>
      );
  }
}

function formatHashrate(hs: number): string {
  if (hs >= 1_000_000) return `${(hs / 1_000_000).toFixed(2)} MH/s`;
  if (hs >= 1_000)     return `${(hs / 1_000).toFixed(1)} KH/s`;
  return `${hs.toFixed(0)} H/s`;
}

const FleetReadout = memo(function FleetReadout() {
  const { agents } = useWebSocket();
  const { online, total, totalHashrate, fillPct } = useMemo(() => {
    const on = agents.filter((a) => a.status === 'online').length;
    const tot = agents.length;
    const hr = agents.reduce((sum, a) => sum + (a.hashrate_15s ?? a.hashrate_15m ?? 0), 0);
    return {
      online: on,
      total: tot,
      totalHashrate: hr,
      fillPct: tot > 0 ? Math.round((on / tot) * 100) : 0,
    };
  }, [agents]);

  // Tick animation: flash hashrate value whenever it meaningfully changes
  const [flash, setFlash] = useState(false);
  const prevHash = useRef(0);
  useEffect(() => {
    if (Math.abs(totalHashrate - prevHash.current) > 1) {
      prevHash.current = totalHashrate;
      setFlash(true);
      const t = setTimeout(() => setFlash(false), 600);
      return () => clearTimeout(t);
    }
  }, [totalHashrate]);

  return (
    <div className="fleet-readout">
      <div className="readout-row">
        <span className={`readout-dot ${online > 0 ? 'readout-dot-online' : 'readout-dot-idle'}`} />
        <span className="readout-label">MACHINES</span>
        <span className="readout-value readout-value-machines">
          {online}<span className="readout-dim">/{total}</span>
        </span>
      </div>
      <div className="readout-row">
        <span className="readout-glyph">≈</span>
        <span className="readout-label">HASHRATE</span>
        <span className={`readout-value ${flash ? 'readout-flash' : ''}`}>
          {totalHashrate > 0 ? formatHashrate(totalHashrate) : <span className="readout-dim">IDLE</span>}
        </span>
      </div>
      <div className="readout-bar-wrap" title={`${online} of ${total} online`}>
        <div className="readout-bar-track">
          <div
            className={`readout-bar-fill ${online > 0 ? 'readout-bar-active' : ''}`}
            style={{ width: `${fillPct}%` }}
          />
        </div>
        <span className="readout-bar-pct">{fillPct}%</span>
      </div>
    </div>
  );
});

const MobileTopStats = memo(function MobileTopStats() {
  const { agents } = useWebSocket();
  const { online, total, hr } = useMemo(() => {
    const on = agents.filter((a) => a.status === 'online').length;
    return {
      online: on,
      total: agents.length,
      hr: agents.reduce((s, a) => s + (a.hashrate_15s ?? a.hashrate_15m ?? 0), 0),
    };
  }, [agents]);
  return (
    <div className="mobile-top-stats">
      <strong>
        {online}/{total} online
      </strong>
      <span>{hr > 0 ? formatHashrate(hr) : 'IDLE'}</span>
    </div>
  );
});

export default function Layout({ children }: LayoutProps) {
  const location = useLocation();
  const isMobile = useIsMobileLayout();
  const { othersOnline, comrades } = usePresence();
  const { glowParticles } = useVisualEffects();
  const [serverConfig, setServerConfig] = useState<ServerConfig | null>(null);
  const [serverInfo, setServerInfo] = useState<ServerInfo | null>(null);
  const [moreOpen, setMoreOpen] = useState(false);

  useEffect(() => {
    Promise.all([api.getConfig(), api.getServerInfo()])
      .then(([cfg, info]) => {
        setServerConfig(cfg);
        setServerInfo(info);
      })
      .catch(() => {});
  }, []);

  useEffect(() => {
    setMoreOpen(false);
  }, [location.pathname]);

  useEffect(() => {
    if (!moreOpen) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.body.style.overflow = prev;
    };
  }, [moreOpen]);

  const setupStatus = getSetupStatus(serverConfig, serverInfo);
  const { latestMessage } = useWebSocket();
  const scoutBiome = useMemo(() => {
    if (latestMessage?.type !== 'scout_constellations') return null;
    return latestMessage.payload as ScoutConstellationSnapshot;
  }, [latestMessage]);
  const pageWeather = useMemo(() => {
    const base = resolvePageWeather(location.pathname);
    const path = location.pathname.split('?')[0].replace(/\/$/, '') || '/';
    if (path === '/emberwake' || path === '/spread' || path === '/dashboard' || path === '/agents') {
      return mergeScoutBiomeWeather(base, scoutBiome);
    }
    return base;
  }, [location.pathname, scoutBiome]);
  const showDeckEffects = isDashboardRoute(location.pathname);
  const moreActive = MOBILE_MORE.some((item) => location.pathname === item.to);
  const mobileShortLabel: Record<string, string> = {
    '/dashboard': 'Deck',
    '/crucible':  'Ops',
    '/activity':  'Feed',
    '/roi':       'ROI',
    '/lotl-timeline': 'Onion',
    '/pathtracer': 'Tracer',
    '/forge':     'Forge',
    '/deploy-recon': 'Scanner',
  };

  return (
    <div
      className={`layout${isMobile ? ' layout--mobile' : ''}${othersOnline ? ' layout--comrades-online' : ''}`}
      data-operator-deck={operatorDeckId(location.pathname)}
    >
      <AmbientBackground weather={pageWeather} />
      {glowParticles && <SacredGeometryLayer />}
      <nav className="sidebar sidebar--desktop desktop-only">
        <div className="sidebar-header">
          <div className="logo">
            <div className="logo-emblem">
              <span className="logo-gear" />
              <img src={afLogo} alt="AetherForge" className="logo-af-img" />
            </div>
            <div className="logo-text-block">
              <span className="logo-text">AetherForge</span>
              <span className="logo-tagline font-tech">COMMAND DECK</span>
            </div>
          </div>
        </div>

        <div className="sidebar-nav">
          {NAV.map((item) => (
            <NavLink
              key={item.to}
              to={item.to}
              title={item.to === '/deploy-recon' ? 'Deploy Recon — browser spread scanner (/browser-spread)' : undefined}
              className={({ isActive }) =>
                `nav-item${'glow' in item && item.glow ? ' nav-item--mission' : ''} ${isActive ? 'active' : ''}`
              }
            >
              <span className="nav-icon">
                <NavIcon type={item.icon} />
              </span>
              <span className="nav-label">{item.label}</span>
              {location.pathname === item.to && (
                <span className={`nav-glow${'glow' in item && item.glow ? ' nav-glow--mission' : ''}`} />
              )}
            </NavLink>
          ))}
          <a
            href={DOCS_HREF}
            target="_blank"
            rel="noopener noreferrer"
            className="nav-item nav-item--docs"
          >
            <span className="nav-icon">
              <NavIcon type="docs" />
            </span>
            <span className="nav-label">Documentation</span>
          </a>
        </div>

        <div className="sidebar-sacred-sigil" aria-hidden>
          <SacredMotif name="flower" opacity={0.6} />
        </div>

        {/* Matrix rain log — Command Deck only; fills lower sidebar between nav and footer */}
        {showDeckEffects && <MatrixRain />}

        <div className="sidebar-footer">
          <FleetReadout />
          {othersOnline && (
            <div className="sidebar-comrades">
              <div className="sidebar-comrades-avatars">
                {comrades.slice(0, 4).map((c) => (
                  <ComradeAvatar key={c.user} user={c.user} size="sm" />
                ))}
              </div>
              <span className="sidebar-comrades-label">
                {comrades.length} comrade{comrades.length === 1 ? '' : 's'} online
              </span>
            </div>
          )}
          <div className="sidebar-sig font-tech">
            <span className="sig-love">made with <span className="sig-heart">♥</span> drjones</span>
            <span className="sig-ver">{formatAppVersion(serverInfo?.version)}</span>
          </div>
        </div>
      </nav>

      {isMobile && (
        <header className="mobile-top-bar">
          <img src={afLogo} alt="" className="mobile-top-logo" />
          <span className="mobile-top-title">AetherForge</span>
          <MobileTopStats />
        </header>
      )}

      <div className={`main-with-status${isMobile ? ' main-with-status--mobile' : ''}`}>
        <SystemStatusBar />
        <SetupBanner status={setupStatus} />
        <main className="main-content">{children}</main>
      </div>

      {isMobile && (
        <>
          {moreOpen && (
            <div
              className="mobile-menu-backdrop"
              role="presentation"
              onClick={() => setMoreOpen(false)}
            />
          )}
          <div className={`mobile-more-sheet${moreOpen ? ' open' : ''}`}>
            {MOBILE_MORE.map((item) => (
              <NavLink
                key={item.to}
                to={item.to}
                className={({ isActive }) => `mobile-more-link${isActive ? ' active' : ''}`}
                onClick={() => setMoreOpen(false)}
              >
                <NavIcon type={item.icon} />
                {item.label}
              </NavLink>
            ))}
            <a
              href={DOCS_HREF}
              target="_blank"
              rel="noopener noreferrer"
              className="mobile-more-link mobile-more-link--docs"
              onClick={() => setMoreOpen(false)}
            >
              <NavIcon type="docs" />
              Documentation
            </a>
          </div>
          <nav className="mobile-bottom-nav" aria-label="Main navigation">
            {MOBILE_PRIMARY.map((item) => (
              <NavLink
                key={item.to}
                to={item.to}
                className={({ isActive }) =>
                  `mobile-bottom-nav-item${isActive ? ' active' : ''}`
                }
              >
                <NavIcon type={item.icon} />
                {mobileShortLabel[item.to] ?? item.label}
              </NavLink>
            ))}
            <button
              type="button"
              className={`mobile-bottom-nav-item${moreActive ? ' active' : ''}`}
              aria-expanded={moreOpen}
              aria-label="More pages"
              onClick={() => setMoreOpen((o) => !o)}
            >
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
                <circle cx="12" cy="5" r="1.5" fill="currentColor" />
                <circle cx="12" cy="12" r="1.5" fill="currentColor" />
                <circle cx="12" cy="19" r="1.5" fill="currentColor" />
              </svg>
              More
            </button>
          </nav>
        </>
      )}
    </div>
  );
}
