﻿import { memo, useEffect, useMemo, useState } from 'react';
import { Link } from 'react-router-dom';
import { api } from '../../api/client';
import { HelpTip } from '../HelpTip';
import {
  FLEET_SPREAD_PORTS,
  PORT_BUNDLES,
  RECON_PORT_HINTS,
  SCAN_PROFILES,
  crucibleSpreadLink,
  curlInstallLine,
  type PortBundleId,
  type ScanProfileId,
} from '../../help/deployRecon';
import type {
  ReconActionRow,
  ReconFormFingerprintCard,
} from '../../help/deployRecon';
import {
  buildAdminSurfaceMap,
  buildFormFingerprintCards,
  buildTechStackHints,
  type AdminSurfaceMapRow,
} from '../../help/deployRecon';
import type {
  ReconPortBanner,
  ReconPortResult,
  ReconScanReport,
  ReconSsrfCanaryInfo,
  ReconStackEntry,
  ReconWebFindingCard,
} from '../../types/recon';
import { ssrfCanaryToken } from '../../help/deployRecon';

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>
  );
}

export const ScanProfileSelector = memo(function ScanProfileSelector({
  value,
  onChange,
}: {
  value: ScanProfileId;
  onChange: (v: ScanProfileId) => void;
}) {
  return (
    <div className="dr-profile-row" data-testid="dr-scan-profile">
      <span className="dr-field-label">Scan profile <HelpTip field="dr_scan_profile" /></span>
      <div className="dr-chip-row">
        {(Object.keys(SCAN_PROFILES) as ScanProfileId[]).map((id) => (
          <button
            key={id}
            type="button"
            className={`dr-chip${value === id ? ' active' : ''}`}
            onClick={() => onChange(id)}
            title={SCAN_PROFILES[id].detail}
          >
            {SCAN_PROFILES[id].label}
          </button>
        ))}
      </div>
    </div>
  );
});

export const PortBundleSelector = memo(function PortBundleSelector({
  value,
  onChange,
}: {
  value: PortBundleId;
  onChange: (v: PortBundleId) => void;
}) {
  return (
    <div className="dr-bundle-row" data-testid="dr-port-bundle">
      <span className="dr-field-label">Port bundle <HelpTip field="dr_port_bundle" /></span>
      <select className="input" value={value} onChange={(e) => onChange(e.target.value as PortBundleId)}>
        {(Object.keys(PORT_BUNDLES) as PortBundleId[]).map((id) => (
          <option key={id} value={id}>
            {PORT_BUNDLES[id].label}
          </option>
        ))}
      </select>
    </div>
  );
});

export const StreamingPortMatrix = memo(function StreamingPortMatrix({
  ports,
  streaming,
  onHint,
}: {
  ports: ReconPortResult[];
  streaming?: boolean;
  onHint: (hint: string) => void;
}) {
  return (
    <div data-testid="dr-port-matrix" className={streaming ? 'dr-port-matrix-streaming' : ''}>
      <h3 className="dr-section-title">
        Port matrix <HelpTip field="dr_port_matrix" />
      </h3>
      <div className="dr-port-matrix">
        {ports.map((p) => (
          <button
            key={p.port}
            type="button"
            className={`dr-port-cell${p.open ? ' open' : ''}${streaming && !p.open ? ' pending' : ''}`}
            disabled={!p.open}
            title={p.open ? RECON_PORT_HINTS[p.port] ?? `TCP ${p.port} open` : `TCP ${p.port} closed`}
            onClick={() => {
              if (p.open) onHint(RECON_PORT_HINTS[p.port] ?? `TCP ${p.port} open`);
            }}
          >
            {p.port}{p.open ? ' *' : ''}
          </button>
        ))}
      </div>
    </div>
  );
});

export const BannerGrabPanel = memo(function BannerGrabPanel({ banners }: { banners: ReconPortBanner[] }) {
  if (!banners?.length) return null;
  return (
    <section className="dr-banner-panel" data-testid="dr-banner-grab">
      <h3 className="dr-section-title">Banner grab <HelpTip field="dr_banner_grab" /></h3>
      <ul className="dr-banner-list">
        {banners.map((b) => (
          <li key={b.port} className="dr-banner-item">
            <span className="font-tech">:{b.port}</span>
            {b.service ? <span className="dr-banner-svc">{b.service}</span> : null}
            {b.title ? <span className="dr-banner-title">{b.title}</span> : null}
            {b.banner ? <code className="dr-banner-code">{b.banner}</code> : null}
            {b.hint ? <span className="dr-banner-hint">{b.hint}</span> : null}
          </li>
        ))}
      </ul>
    </section>
  );
});

export const FormFingerprintCards = memo(function FormFingerprintCards({
  cards,
}: {
  cards: ReconFormFingerprintCard[];
}) {
  if (cards.length === 0) return null;
  return (
    <section data-testid="dr-form-fingerprint">
      <h3 className="dr-section-title">Form fingerprints <HelpTip field="dr_form_fingerprint" /></h3>
      <div className="dr-fp-grid">
        {cards.map((c) => (
          <article key={c.id} className="dr-fp-card">
            <div className="dr-fp-head">
              <span className="font-tech">{c.name}</span>
              <span className="dr-fp-score">score {c.score}</span>
            </div>
            <p className="dr-fp-url">{c.page_url}</p>
            {c.matches.length ? <p className="dr-fp-matches">{c.matches.join(' · ')}</p> : null}
            <CopyChip text={c.paste_target} label="Copy paste target" />
          </article>
        ))}
      </div>
    </section>
  );
});

export const SsrfCanaryPanel = memo(function SsrfCanaryPanel({
  canary,
  scanId,
  wsHitScanId,
}: {
  canary?: ReconSsrfCanaryInfo;
  scanId?: string;
  wsHitScanId?: string | null;
}) {
  const token = ssrfCanaryToken(canary?.scan_id || scanId || '');
  const [status, setStatus] = useState(canary?.status ?? 'pending');
  const [hitAt, setHitAt] = useState(canary?.hit_at);

  useEffect(() => {
    if (canary?.status) setStatus(canary.status);
    if (canary?.hit_at) setHitAt(canary.hit_at);
  }, [canary]);

  useEffect(() => {
    if (wsHitScanId && token && wsHitScanId === token) {
      setStatus('confirmed');
      setHitAt(new Date().toISOString());
    }
  }, [wsHitScanId, token]);

  useEffect(() => {
    if (!token || status === 'confirmed') return undefined;
    const timer = setInterval(() => {
      void api.getSsrfCanaryStatus(token).then((info) => {
        if (!info) return;
        setStatus(info.status);
        if (info.hit_at) setHitAt(info.hit_at);
      });
    }, 4000);
    return () => clearInterval(timer);
  }, [token, status]);

  const url = canary?.url;
  if (!url && !token) return null;

  return (
    <section className="dr-canary-panel" data-testid="dr-ssrf-canary">
      <h3 className="dr-section-title">
        SSRF canary <HelpTip field="dr_ssrf_canary" />
        <span className={`dr-canary-badge ${status}`}>{status}</span>
      </h3>
      {url ? <CopyChip text={url} label="Copy canary URL" /> : null}
      {hitAt ? <p className="dr-canary-hit">Hit at {hitAt}</p> : null}
      {canary?.paste_field_name ? (
        <p className="dr-empty">Paste into field: {canary.paste_field_name}</p>
      ) : null}
    </section>
  );
});

export const UploadAdminMap = memo(function UploadAdminMap({ rows }: { rows: AdminSurfaceMapRow[] }) {
  if (!rows.length) return null;
  return (
    <section data-testid="dr-upload-admin-map">
      <h3 className="dr-section-title">Upload / admin map <HelpTip field="dr_upload_admin_map" /></h3>
      <table className="dr-admin-table">
        <thead>
          <tr>
            <th>Path</th>
            <th>Status</th>
            <th>Signal</th>
          </tr>
        </thead>
        <tbody>
          {rows.map((r) => {
            const live = r.status_code >= 200 && r.status_code < 400;
            return (
              <tr key={r.url} className={live ? 'dr-admin-live' : 'dr-admin-muted'}>
                <td>
                  <a href={r.url} target="_blank" rel="noreferrer">
                    {r.path}
                  </a>
                </td>
                <td>{r.status_code || '—'}</td>
                <td>{r.signal}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </section>
  );
});

export const TechStackBanner = memo(function TechStackBanner({ stack }: { stack: ReconStackEntry[] }) {
  if (!stack.length) return null;
  return (
    <section data-testid="dr-tech-stack">
      <h3 className="dr-section-title">Tech stack <HelpTip field="dr_tech_stack" /></h3>
      <ul className="dr-stack-list">
        {stack.map((e) => (
          <li key={`${e.name}-${e.source}`}>
            <strong>{e.name}</strong>
            <span className="dr-stack-src">{e.source}</span>
            {e.detail ? <span className="dr-stack-detail">{e.detail}</span> : null}
          </li>
        ))}
      </ul>
    </section>
  );
});

export const ActionMatrix = memo(function ActionMatrix({
  rows,
  onOpenPlaybook,
  onAction,
}: {
  rows: ReconActionRow[];
  onOpenPlaybook?: (lane: string) => void;
  onAction?: (lane: string, action: string) => void;
}) {
  if (!rows.length) return null;
  return (
    <section data-testid="dr-action-matrix">
      <h3 className="dr-section-title">Action matrix <HelpTip field="dr_action_matrix" /></h3>
      <table className="dr-action-table">
        <thead>
          <tr>
            <th>Lane</th>
            <th>Reason</th>
            <th>Actions</th>
          </tr>
        </thead>
        <tbody>
          {rows.map((r) => (
            <tr key={r.id}>
              <td className="font-tech">{r.title}</td>
              <td>{r.reason}</td>
              <td>
                <div className="dr-action-chips">
                  {r.actions.map((a) => (
                    <button
                      key={a}
                      type="button"
                      className="btn btn-outline btn-sm"
                      onClick={() => {
                        if (a === 'playbook' && onOpenPlaybook) onOpenPlaybook(r.lane);
                        onAction?.(r.lane, a);
                      }}
                    >
                      {a}
                    </button>
                  ))}
                </div>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </section>
  );
});

export const FindingCard = memo(function FindingCard({
  card,
  curlLine,
  host,
  fleetSpreadOpen,
  deployKitLane,
}: {
  card: ReconWebFindingCard;
  curlLine: string;
  host: string;
  fleetSpreadOpen: boolean;
  deployKitLane?: string;
}) {
  const finding = deployKitLane || card.spread_lane;
  return (
    <article className="dr-finding-card" data-testid={`dr-finding-${card.id}`}>
      <div className="dr-finding-head">
        <span className="dr-finding-title">{card.title}</span>
        <span className={`dr-confidence ${card.confidence}`}>{card.confidence}</span>
        {card.spread_lane ? <span className="join-lane-badge">{card.spread_lane}</span> : null}
      </div>
      <p className="dr-finding-detail">{card.detail}</p>
      {card.mermaid ? <pre className="dr-mermaid">{card.mermaid}</pre> : null}
      <div className="dr-deploy-panel">
        <CopyChip text={curlLine} label="Copy curl install.sh" />
        {card.probe_url ? <CopyChip text={card.probe_url} label="Copy SSRF probe URL" /> : null}
        {fleetSpreadOpen ? (
          <Link to={crucibleSpreadLink(host, finding)} className="btn btn-outline btn-sm">
            Fleet spread to {host}
          </Link>
        ) : null}
      </div>
    </article>
  );
});

export function useDeployReconDerived(report: ReconScanReport | null, deckOrigin: string) {
  return useMemo(() => {
    if (!report) {
      return {
        fingerprintCards: [] as ReconFormFingerprintCard[],
        adminRows: [] as AdminSurfaceMapRow[],
        stack: [] as ReconStackEntry[],
      };
    }
    return {
      fingerprintCards: buildFormFingerprintCards(report, deckOrigin),
      adminRows: buildAdminSurfaceMap(report),
      stack: buildTechStackHints(report),
    };
  }, [report, deckOrigin]);
}


