import { Fragment, useCallback, useEffect, useState } from "react";
import { useAuth } from "../context/AuthContext";
import { api, ApiError, type EscalationDetail, type EscalationSummary } from "../api/client";

export function StaffEscalationQueuePage() {
  const { staffKey, setStaffKey } = useAuth();
  const [escalations, setEscalations] = useState<EscalationSummary[] | null>(null);
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [detail, setDetail] = useState<EscalationDetail | null>(null);
  const [loadError, setLoadError] = useState<string | null>(null);

  // A rejected/revoked staff key (401/403) signs out — from either load, so a
  // key revoked mid-session doesn't leave every click failing; anything else
  // says so. Neither used to be handled: the queue just sat on "Loading…".
  const handleLoadError = useCallback(
    (err: unknown, message: string) => {
      if (err instanceof ApiError && (err.status === 401 || err.status === 403)) setStaffKey(null);
      else setLoadError(message);
    },
    [setStaffKey]
  );

  useEffect(() => {
    if (!staffKey) return;
    let cancelled = false;
    api
      .listEscalations(staffKey)
      .then((data) => {
        if (cancelled) return;
        setEscalations(data);
        setLoadError(null);
      })
      .catch((err) => !cancelled && handleLoadError(err, "Couldn't load the escalation queue. Reload to try again."));
    return () => {
      cancelled = true;
    };
  }, [staffKey, handleLoadError]);

  useEffect(() => {
    if (!staffKey || !selectedId) {
      setDetail(null);
      return;
    }
    let cancelled = false;
    api
      .getEscalation(staffKey, selectedId)
      .then((data) => {
        if (cancelled) return;
        setDetail(data);
        setLoadError(null); // a later success clears an earlier failure's alert
      })
      .catch((err) => !cancelled && handleLoadError(err, "Couldn't load that escalation."));
    return () => {
      cancelled = true;
    };
  }, [staffKey, selectedId, handleLoadError]);

  async function refreshList() {
    if (!staffKey) return;
    setEscalations(await api.listEscalations(staffKey));
  }

  return (
    <div style={{ display: "grid", gridTemplateColumns: "360px 1fr", gap: 24 }}>
      <div>
        <h1 className="page-title">Escalation Queue</h1>
        {loadError && (
          <p role="alert" className="page-subtitle">
            {loadError}
          </p>
        )}
        {escalations === null ? (
          <p className="page-subtitle">Loading…</p>
        ) : escalations.length === 0 ? (
          <div className="card empty-state">Nothing escalated right now.</div>
        ) : (
          <div className="table-wrap">
            <table>
              <tbody>
                {escalations.map((e) => (
                  <tr
                    key={e.id}
                    onClick={() => setSelectedId(e.id)}
                    style={{ cursor: "pointer", background: e.id === selectedId ? "var(--surface-raised)" : undefined }}
                  >
                    <td>
                      <div className="mono" style={{ fontSize: 12 }}>
                        {e.job_id.slice(0, 8)} / {e.dump_id.slice(0, 8)}
                      </div>
                      <div style={{ fontSize: 12, color: "var(--text-muted)" }}>{e.reason}</div>
                    </td>
                    <td>
                      <span className={`badge ${e.resolved_at ? "badge--ok" : "badge--critical"}`}>
                        {e.resolved_at ? "Resolved" : "Open"}
                      </span>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </div>

      <div>
        {detail ? (
          <EscalationDetailPanel key={detail.id} detail={detail} onResolved={refreshList} />
        ) : (
          <div className="card empty-state">Select an escalation to view its transcript and resolve it.</div>
        )}
      </div>
    </div>
  );
}

export function formatDumpType(dumpType: string): string {
  return dumpType === "kernel_mode" ? "Kernel-mode" : "User-mode";
}

// A fence one backtick longer than any run already in the payload, so a
// finding containing literal ``` can't terminate the block early.
function jsonCodeFence(json: string): string {
  const longestRun = Math.max(2, ...(json.match(/`+/g) ?? [""]).map((run) => run.length));
  return "`".repeat(longestRun + 1);
}

function markdownCodeBlock(json: string): string {
  const fence = jsonCodeFence(json);
  return [`${fence}json`, json, fence].join("\n");
}

// --- Tier 1 / Tier 2 findings rendering --------------------------------
//
// tier1_findings/tier2_findings come across the wire as `dict | None`
// (schemas/escalations.py), not validated against the Tier1Findings/
// Tier2Findings pydantic models (app/triage/schemas.py) at read time. So
// this renders the fields that schema defines today, and — instead of
// assuming that's everything — puts anything else the dict happens to
// carry under "Additional fields" rather than dropping it, in case the
// two schemas ever drift.

type FieldKind = "text" | "link" | "paragraph" | "list" | "code-list";

interface FieldSpec {
  key: string;
  label: string;
  kind: FieldKind;
}

// Order and labels mirror Tier1Findings in app/triage/schemas.py.
const TIER1_FIELDS: FieldSpec[] = [
  { key: "bugcheck_or_exception_code", label: "Bugcheck / exception code", kind: "text" },
  { key: "faulting_module", label: "Faulting module", kind: "text" },
  { key: "failure_bucket_id", label: "Failure bucket", kind: "text" },
  { key: "bugcheck_reference_url", label: "Bug-check reference", kind: "link" },
  { key: "summary", label: "Summary", kind: "paragraph" },
  { key: "top_of_stack", label: "Top of stack", kind: "code-list" },
  { key: "notable_observations", label: "Notable observations", kind: "list" },
  { key: "expected_symbol_info", label: "Expected symbol info", kind: "paragraph" },
];

// Order and labels mirror Tier2Findings in app/triage/schemas.py.
const TIER2_FIELDS: FieldSpec[] = [
  { key: "faulting_module", label: "Faulting module", kind: "text" },
  { key: "root_cause_hypothesis", label: "Root cause hypothesis", kind: "paragraph" },
  { key: "evidence", label: "Evidence", kind: "list" },
  { key: "open_questions", label: "Open questions", kind: "list" },
];

function humanize(key: string): string {
  return key.replace(/_/g, " ").replace(/^./, (c) => c.toUpperCase());
}

function isEmpty(value: unknown): boolean {
  return value === null || value === undefined || value === "" || (Array.isArray(value) && value.length === 0);
}

function formatSignalValue(value: unknown): string {
  if (typeof value === "boolean") return value ? "yes" : "no";
  if (typeof value === "number") return Number.isInteger(value) ? String(value) : value.toFixed(2);
  if (value === null || value === undefined) return "—";
  return String(value);
}

interface ClassifiedFindings {
  meta: { field: FieldSpec; value: string }[];
  body: { field: FieldSpec; value: string | string[] }[];
  signals: Record<string, unknown> | null;
  activeFlags: string[];
  hasFlags: boolean;
  extras: Record<string, unknown>;
}

// Single source of truth for both findingsToMarkdown and FindingsView, so
// the two never disagree about what counts as "known" vs. "extra". A
// list/code-list field whose stored value isn't actually an array (the API
// types tier1_findings/tier2_findings as an unvalidated `dict | None`, so
// its shape isn't guaranteed to match the current Tier1Findings/
// Tier2Findings pydantic model) is left unconsumed here and falls through
// to `extras` instead of being force-cast and crashing on iteration.
function classifyFindings(findings: Record<string, unknown>, fields: FieldSpec[]): ClassifiedFindings {
  const consumed = new Set<string>();
  const meta: ClassifiedFindings["meta"] = [];
  const body: ClassifiedFindings["body"] = [];

  for (const f of fields) {
    const value = findings[f.key];
    if (isEmpty(value)) continue;
    if (f.kind === "text" || f.kind === "link") {
      meta.push({ field: f, value: String(value) });
      consumed.add(f.key);
    } else if (f.kind === "paragraph") {
      body.push({ field: f, value: String(value) });
      consumed.add(f.key);
    } else if (Array.isArray(value)) {
      body.push({ field: f, value: value.map((item) => String(item)) });
      consumed.add(f.key);
    }
  }

  let signals: Record<string, unknown> | null = null;
  if (findings.confidence_signals && typeof findings.confidence_signals === "object") {
    signals = findings.confidence_signals as Record<string, unknown>;
    consumed.add("confidence_signals");
  }

  let hasFlags = false;
  let activeFlags: string[] = [];
  if (findings.hard_escalate_flags && typeof findings.hard_escalate_flags === "object") {
    hasFlags = true;
    activeFlags = Object.entries(findings.hard_escalate_flags as Record<string, unknown>)
      .filter(([, v]) => v === true)
      .map(([k]) => k);
    consumed.add("hard_escalate_flags");
  }

  const extras: Record<string, unknown> = {};
  for (const [k, v] of Object.entries(findings)) {
    if (consumed.has(k) || isEmpty(v)) continue;
    extras[k] = v;
  }

  return { meta, body, signals, activeFlags, hasFlags, extras };
}

function findingsToMarkdown(findings: Record<string, unknown>, fields: FieldSpec[]): string {
  const { meta, body, signals, activeFlags, hasFlags, extras } = classifyFindings(findings, fields);
  const lines: string[] = [];
  if (meta.length) {
    for (const { field, value } of meta) lines.push(`- **${field.label}:** ${value}`);
    lines.push("");
  }
  for (const { field, value } of body) {
    lines.push(`**${field.label}**`, "");
    if (typeof value === "string") {
      lines.push(value, "");
    } else {
      for (const item of value) lines.push(field.kind === "code-list" ? `- \`${item}\`` : `- ${item}`);
      lines.push("");
    }
  }
  if (signals) {
    lines.push("**Confidence signals**", "");
    for (const [k, v] of Object.entries(signals)) {
      if (v === null || v === undefined) continue;
      lines.push(`- ${humanize(k)}: ${formatSignalValue(v)}`);
    }
    lines.push("");
  }
  if (hasFlags) {
    lines.push("**Hard-escalate flags**", "");
    lines.push(activeFlags.length ? activeFlags.map((k) => `- ${humanize(k)}`).join("\n") : "- None set");
    lines.push("");
  }
  if (Object.keys(extras).length) {
    lines.push("**Additional fields**", "", markdownCodeBlock(JSON.stringify(extras, null, 2)));
  }
  return lines.join("\n").trim();
}

function tier1Markdown(findings: Record<string, unknown>): string {
  return findingsToMarkdown(findings, TIER1_FIELDS);
}

function tier2Markdown(findings: Record<string, unknown>): string {
  return findingsToMarkdown(findings, TIER2_FIELDS);
}

const fieldHeadingStyle = {
  fontSize: 12,
  textTransform: "uppercase" as const,
  letterSpacing: 0.4,
  color: "var(--text-muted)",
  margin: "0 0 4px",
};

function FindingsView({ findings, fields }: { findings: Record<string, unknown>; fields: FieldSpec[] }) {
  const { meta, body, signals, activeFlags, hasFlags, extras } = classifyFindings(findings, fields);

  return (
    <div style={{ fontSize: 13, lineHeight: 1.5 }}>
      {meta.length > 0 && (
        <dl className="kv-grid" style={{ marginBottom: 12 }}>
          {meta.map(({ field, value }) => (
            <Fragment key={field.key}>
              <dt>{field.label}</dt>
              <dd>
                {field.kind === "link" ? (
                  <a href={value} target="_blank" rel="noreferrer">
                    {value}
                  </a>
                ) : (
                  value
                )}
              </dd>
            </Fragment>
          ))}
        </dl>
      )}
      {body.map(({ field, value }) => (
        <div key={field.key} style={{ marginBottom: 12 }}>
          <h4 style={fieldHeadingStyle}>{field.label}</h4>
          {typeof value === "string" ? (
            <p style={{ margin: 0 }}>{value}</p>
          ) : (
            <ul style={{ margin: 0, paddingLeft: 18 }}>
              {value.map((item, i) => (
                <li key={i} className={field.kind === "code-list" ? "mono" : undefined}>
                  {item}
                </li>
              ))}
            </ul>
          )}
        </div>
      ))}
      {signals && (
        <div style={{ marginBottom: 12 }}>
          <h4 style={fieldHeadingStyle}>Confidence signals</h4>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
            {Object.entries(signals)
              .filter(([, v]) => v !== null && v !== undefined)
              .map(([k, v]) => (
                <span key={k} className="badge" style={{ fontSize: 11 }}>
                  {humanize(k)}: {formatSignalValue(v)}
                </span>
              ))}
          </div>
        </div>
      )}
      {hasFlags && (
        <div style={{ marginBottom: 12 }}>
          <h4 style={fieldHeadingStyle}>Hard-escalate flags</h4>
          {activeFlags.length ? (
            <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
              {activeFlags.map((k) => (
                <span key={k} className="badge badge--critical" style={{ fontSize: 11 }}>
                  {humanize(k)}
                </span>
              ))}
            </div>
          ) : (
            <span style={{ color: "var(--text-muted)" }}>None set</span>
          )}
        </div>
      )}
      {Object.keys(extras).length > 0 && (
        <div>
          <h4 style={fieldHeadingStyle}>Additional fields</h4>
          <pre className="mono" style={{ fontSize: 12, whiteSpace: "pre-wrap", background: "var(--bg)", padding: 12, borderRadius: 6, margin: 0 }}>
            {JSON.stringify(extras, null, 2)}
          </pre>
        </div>
      )}
    </div>
  );
}

export function escalationToMarkdown(detail: EscalationDetail): string {
  const lines = [
    `# Escalation ${detail.job_id.slice(0, 8)}`,
    "",
    `- **Dump:** ${detail.dump_id.slice(0, 8)}`,
    `- **Type:** ${formatDumpType(detail.dump_type)}`,
    `- **Reason:** ${detail.reason}`,
  ];
  if (detail.tier1_findings) {
    lines.push("", "## Tier 1 findings", "", tier1Markdown(detail.tier1_findings));
  }
  if (detail.tier2_findings) {
    lines.push("", "## Tier 2 findings", "", tier2Markdown(detail.tier2_findings));
  }
  if (detail.resolved_at) {
    const tag = detail.root_cause_tag ? ` [${detail.root_cause_tag}]` : "";
    lines.push("", `**Resolved** by ${detail.assigned_engineer}${tag} — ${detail.resolution}`);
  }
  return lines.join("\n");
}

async function copyToClipboard(text: string) {
  if (navigator.clipboard?.writeText) {
    await navigator.clipboard.writeText(text);
    return;
  }
  const textarea = document.createElement("textarea");
  textarea.value = text;
  textarea.style.position = "fixed";
  textarea.style.opacity = "0";
  document.body.appendChild(textarea);
  textarea.select();
  const ok = document.execCommand("copy");
  document.body.removeChild(textarea);
  if (!ok) throw new Error("copy command failed");
}

function CopyButton({ getText, label = "Copy" }: { getText: () => string; label?: string }) {
  const [copied, setCopied] = useState(false);

  async function handleClick() {
    try {
      await copyToClipboard(getText());
      setCopied(true);
      setTimeout(() => setCopied(false), 1500);
    } catch {
      // clipboard access denied or unavailable — nothing to recover into
    }
  }

  return (
    <button type="button" className="button button--ghost" style={{ padding: "4px 10px", fontSize: 12 }} onClick={handleClick}>
      {copied ? "Copied!" : label}
    </button>
  );
}

function EscalationDetailPanel({
  detail,
  onResolved,
}: {
  detail: EscalationDetail;
  onResolved: () => void;
}) {
  const { staffKey } = useAuth();
  const [engineer, setEngineer] = useState("");
  const [resolution, setResolution] = useState("");
  const [rootCauseTag, setRootCauseTag] = useState("");
  const [kbOptIn, setKbOptIn] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function handleResolve() {
    if (!staffKey || !engineer.trim() || !resolution.trim()) return;
    setSubmitting(true);
    setError(null);
    try {
      await api.resolveEscalation(staffKey, detail.id, {
        assigned_engineer: engineer.trim(),
        resolution: resolution.trim(),
        root_cause_tag: rootCauseTag.trim() || undefined,
        kb_opt_in: kbOptIn,
      });
      onResolved();
    } catch (err) {
      setError(err instanceof ApiError ? err.message : "Failed to resolve — try again.");
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <div className="card">
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <h2 style={{ margin: 0, fontSize: 15 }}>
          Escalation <span className="mono">{detail.job_id.slice(0, 8)}</span>
        </h2>
        <CopyButton label="Copy as Markdown" getText={() => escalationToMarkdown(detail)} />
      </div>
      <dl className="kv-grid" style={{ marginBottom: 20 }}>
        <dt>Dump</dt>
        <dd className="mono">{detail.dump_id.slice(0, 8)}</dd>
        <dt>Type</dt>
        <dd>{formatDumpType(detail.dump_type)}</dd>
        <dt>Reason</dt>
        <dd>{detail.reason}</dd>
        {detail.incident_io_url && (
          <>
            <dt>Incident</dt>
            <dd>
              <a href={detail.incident_io_url} target="_blank" rel="noreferrer">
                Open in incident.io
              </a>
            </dd>
          </>
        )}
      </dl>

      {detail.tier1_findings && (
        <>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
            <h3 style={{ fontSize: 13, color: "var(--text-muted)", margin: 0 }}>Tier 1 findings</h3>
            <div style={{ display: "flex", gap: 6 }}>
              <CopyButton label="Copy as Markdown" getText={() => tier1Markdown(detail.tier1_findings!)} />
              <CopyButton label="Copy as JSON" getText={() => JSON.stringify(detail.tier1_findings, null, 2)} />
            </div>
          </div>
          <FindingsView findings={detail.tier1_findings} fields={TIER1_FIELDS} />
        </>
      )}
      {detail.tier2_findings && (
        <>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
            <h3 style={{ fontSize: 13, color: "var(--text-muted)", margin: 0 }}>Tier 2 findings</h3>
            <div style={{ display: "flex", gap: 6 }}>
              <CopyButton label="Copy as Markdown" getText={() => tier2Markdown(detail.tier2_findings!)} />
              <CopyButton label="Copy as JSON" getText={() => JSON.stringify(detail.tier2_findings, null, 2)} />
            </div>
          </div>
          <FindingsView findings={detail.tier2_findings} fields={TIER2_FIELDS} />
        </>
      )}

      {detail.resolved_at ? (
        <div className="field-hint">
          Resolved by {detail.assigned_engineer}
          {detail.root_cause_tag ? ` [${detail.root_cause_tag}]` : ""} — {detail.resolution}
        </div>
      ) : (
        <div style={{ marginTop: 20, borderTop: "1px solid var(--border)", paddingTop: 20 }}>
          <h3 style={{ fontSize: 13, marginTop: 0 }}>Resolve</h3>
          <div className="field">
            <label>Your name</label>
            <input type="text" value={engineer} onChange={(e) => setEngineer(e.target.value)} />
          </div>
          <div className="field">
            <label>Root cause tag</label>
            <input type="text" value={rootCauseTag} onChange={(e) => setRootCauseTag(e.target.value)} placeholder="e.g. driver-race-condition" />
          </div>
          <div className="field">
            <label>Resolution</label>
            <textarea value={resolution} onChange={(e) => setResolution(e.target.value)} rows={4} />
          </div>
          <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13, marginBottom: 16 }}>
            <input type="checkbox" checked={kbOptIn} onChange={(e) => setKbOptIn(e.target.checked)} />
            Feed this resolution into the knowledge base
          </label>
          {error && <p className="field-error">{error}</p>}
          <button className="button" disabled={submitting || !engineer.trim() || !resolution.trim()} onClick={handleResolve}>
            {submitting ? "Resolving…" : "Mark resolved"}
          </button>
        </div>
      )}
    </div>
  );
}
