/**
 * @file Workflows.tsx
 * @description Displays comprehensive analytics on agent orchestration patterns, including DAGs of agent spawning, tool usage flows, collaboration networks, and session complexity metrics, with real-time updates and interactive filtering.
 * @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
 */
/* =============================================================================
 * MODULE_GUIDE — extended in-file reference (comments only; safe to read, never executed)
 * =============================================================================
 * **Purpose:** Workflow analytics visualization built on D3; consumes aggregated session/run metrics from the workflows API.
 *
 * ## Design constraints
 * - Local-first: no telemetry leaves the machine unless the user configures webhooks.
 * - Fail-safe hooks path on the server must never block Claude Code; UI mirrors that
 *   philosophy by degrading gracefully (empty states, stale badges, reconnect loops).
 * - Destructive flows stay behind explicit confirmation modals and server-side gates.
 * - Internationalization: user-visible strings belong in i18n JSON, not literals here.
 *
 * ## Remote data & SSH
 * Remote Data Sources let operators aggregate multiple machines. SSH entries describe
 * how to reach a peer dashboard; the global data scope (`dataScope.ts`) narrows every
 * scoped GET via `?sources=`. Health checks and import history surface in Settings.
 *
 * ## Internal dependencies
 * - `../lib/api`
 * - `../lib/eventBus`
 * - `../lib/types`
 * - `../components/workflows/WorkflowStats`
 * - `../components/workflows/OrchestrationDAG`
 * - `../components/workflows/ToolExecutionFlow`
 * - `../components/workflows/AgentCollaborationNetwork`
 * - `../components/workflows/SubagentEffectiveness`
 * - `../components/workflows/WorkflowPatterns`
 * - `../components/workflows/ModelDelegationFlow`
 * - `../components/workflows/ErrorPropagationMap`
 * - `../components/workflows/ConcurrencyTimeline`
 *
 * ## Public surface
 * - `Workflows` — exported API; see TSDoc on the symbol for behavior.
 *
 * ## Testing pointers
 * - Prefer colocated `__tests__` with Vitest + Testing Library for UI.
 * - Server contract changes require `npm run test:server` and OpenAPI sync.
 * - MCP edits: `npm run mcp:typecheck` and `npm run mcp:build`.
 *
 * ## Related docs
 * - `ARCHITECTURE.md` — hooks → API → SQLite → WebSocket → UI pipeline.
 * - `docs/API.md` — REST reference.
 * - `.claude/skills/file-headers/` — mandatory `@author` header policy.
 * ============================================================================= */
/* -----------------------------------------------------------------------------
 * EXPORT CATALOG — quick index of symbols defined below (documentation only).
 * -----------------------------------------------------------------------------
 * **Workflows**
 *   Part of this module's public contract. Downstream imports should treat
 *   the signature and return type as stable unless release notes say otherwise.
 *   When behavior changes, update the `@file` overview and relevant tests.
 *
 * ----------------------------------------------------------------------------- */

import {
  useCallback,
  useEffect,
  useLayoutEffect,
  useRef,
  useState,
  useSyncExternalStore,
} from "react";
import { useTranslation } from "react-i18next";
import { Workflow, RefreshCw, Download, AlertCircle, Info } from "lucide-react";
import { api } from "../lib/api";
import { eventBus } from "../lib/eventBus";
import type { WorkflowData, WSMessage } from "../lib/types";

import { WorkflowStats } from "../components/workflows/WorkflowStats";
import { OrchestrationDAG } from "../components/workflows/OrchestrationDAG";
import { ToolExecutionFlow } from "../components/workflows/ToolExecutionFlow";
import { AgentCollaborationNetwork } from "../components/workflows/AgentCollaborationNetwork";
import { SubagentEffectiveness } from "../components/workflows/SubagentEffectiveness";
import { WorkflowPatterns } from "../components/workflows/WorkflowPatterns";
import { ModelDelegationFlow } from "../components/workflows/ModelDelegationFlow";
import { ErrorPropagationMap } from "../components/workflows/ErrorPropagationMap";
import { ConcurrencyTimeline } from "../components/workflows/ConcurrencyTimeline";
import { SessionComplexityScatter } from "../components/workflows/SessionComplexityScatter";
import { CompactionImpact } from "../components/workflows/CompactionImpact";
import { SessionDrillIn } from "../components/workflows/SessionDrillIn";
import { WorkflowRunsPanel } from "../components/workflows/WorkflowRunsPanel";

type StatusFilter = "all" | "active" | "completed";

export function Workflows() {
  const { t } = useTranslation("workflows");
  const [data, setData] = useState<WorkflowData | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [selectedNode, setSelectedNode] = useState<string | null>(null);
  const [selectedSessionId, setSelectedSessionId] = useState<string | null>(null);
  const [statusFilter, setStatusFilter] = useState<StatusFilter>("all");
  const [lastUpdated, setLastUpdated] = useState<Date | null>(null);

  const fetchData = useCallback(async () => {
    try {
      setError(null);
      const result = await api.workflows.get(statusFilter);
      setData(result);
      setLastUpdated(new Date());
    } catch (err) {
      setError(err instanceof Error ? err.message : t("failedLoad"));
    } finally {
      setLoading(false);
    }
  }, [statusFilter]);

  useEffect(() => {
    fetchData();
  }, [fetchData]);

  // Auto-refresh on WebSocket events
  useEffect(() => {
    let debounceTimer: ReturnType<typeof setTimeout>;
    const handler = (_msg: WSMessage) => {
      clearTimeout(debounceTimer);
      debounceTimer = setTimeout(fetchData, 3000);
    };
    const unsub = eventBus.subscribe(handler);
    return () => {
      unsub();
      clearTimeout(debounceTimer);
    };
  }, [fetchData]);

  const handleRefresh = () => {
    setLoading(true);
    fetchData();
  };

  const handleExport = () => {
    if (!data) return;
    const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `workflows-${new Date().toISOString().slice(0, 10)}.json`;
    a.click();
    URL.revokeObjectURL(url);
  };

  if (loading && !data) {
    return (
      <div className="space-y-6">
        <PageHeader
          statusFilter={statusFilter}
          onStatusFilterChange={setStatusFilter}
          onRefresh={handleRefresh}
          onExport={handleExport}
          lastUpdated={null}
        />
        <div className="grid grid-cols-2 sm:grid-cols-3 xl:grid-cols-6 gap-3">
          {Array.from({ length: 6 }).map((_, i) => (
            <div key={i} className="card h-24 animate-pulse bg-surface-2" />
          ))}
        </div>
        {Array.from({ length: 4 }).map((_, i) => (
          <div key={i} className="card h-64 animate-pulse bg-surface-2" />
        ))}
      </div>
    );
  }

  if (error && !data) {
    return (
      <div className="space-y-6">
        <PageHeader
          statusFilter={statusFilter}
          onStatusFilterChange={setStatusFilter}
          onRefresh={handleRefresh}
          onExport={handleExport}
          lastUpdated={null}
        />
        <div className="card flex flex-col items-center justify-center py-16 gap-4">
          <AlertCircle className="w-10 h-10 text-status-danger" />
          <p className="text-status-danger text-sm">{error}</p>
          <button onClick={handleRefresh} className="btn-primary text-sm">
            {t("common:retry")}
          </button>
        </div>
      </div>
    );
  }

  if (!data) return null;

  return (
    <div className="space-y-6">
      {/* Page Header */}
      <PageHeader
        statusFilter={statusFilter}
        onStatusFilterChange={setStatusFilter}
        onRefresh={handleRefresh}
        onExport={handleExport}
        lastUpdated={lastUpdated}
      />

      {/* Stats Row */}
      <WorkflowStats stats={data.stats} />

      {/* Workflow-tool runs (issue #167) - fleets ingested from on-disk journals */}
      <div className="card p-4 space-y-3">
        <div>
          <h2 className="text-sm font-semibold text-fg-secondary flex items-center gap-2">
            <Workflow className="w-4 h-4 text-violet-400" />
            {t("runs.title")}
          </h2>
          <p className="text-xs text-fg-muted mt-0.5">{t("runs.subtitle")}</p>
        </div>
        <WorkflowRunsPanel statusFilter={statusFilter} />
      </div>

      {/* Section 1: Agent Orchestration DAG */}
      <Section
        number={1}
        title={t("orchestration.title")}
        subtitle={t("orchestration.subtitle")}
        infoKey="orchestration"
      >
        <OrchestrationDAG
          data={data.orchestration}
          onNodeClick={setSelectedNode}
          selectedNode={selectedNode}
        />
        {selectedNode && (
          <div className="mt-3 flex items-center gap-2">
            <span className="text-xs text-fg-muted">{t("filteredBy")}</span>
            <span className="badge bg-accent/15 text-accent border border-accent/20 text-xs">
              {selectedNode}
            </span>
            <button
              onClick={() => setSelectedNode(null)}
              className="text-xs text-fg-muted hover:text-fg-secondary underline"
            >
              {t("clearFilter")}
            </button>
          </div>
        )}
      </Section>

      {/* Section 2: Tool Execution Flow */}
      <Section
        number={2}
        title={t("toolFlow.title")}
        subtitle={t("toolFlow.subtitle")}
        infoKey="toolFlow"
      >
        <ToolExecutionFlow data={data.toolFlow} filterAgentType={selectedNode} />
      </Section>

      {/* Section 3: Agent Collaboration Network */}
      <Section
        number={3}
        title={t("pipeline.title")}
        subtitle={t("pipeline.subtitle")}
        infoKey="pipeline"
      >
        <AgentCollaborationNetwork effectiveness={data.effectiveness} edges={data.cooccurrence} />
      </Section>

      {/* Section 4 + 5: Two Column */}
      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
        <Section
          number={4}
          title={t("effectiveness.title")}
          subtitle={t("effectiveness.subtitle")}
          infoKey="effectiveness"
        >
          <SubagentEffectiveness data={data.effectiveness} />
        </Section>

        <Section
          number={5}
          title={t("patterns.title")}
          subtitle={t("patterns.subtitle")}
          infoKey="patterns"
        >
          <WorkflowPatterns data={data.patterns} onPatternClick={() => {}} />
        </Section>
      </div>

      {/* Section 6 + 7: Two Column */}
      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
        <Section
          number={6}
          title={t("modelDelegation.title")}
          subtitle={t("modelDelegation.subtitle")}
          infoKey="modelDelegation"
        >
          <ModelDelegationFlow data={data.modelDelegation} />
        </Section>

        <Section
          number={7}
          title={t("errorPropagation.title")}
          subtitle={t("errorPropagation.subtitle")}
          infoKey="errorPropagation"
        >
          <ErrorPropagationMap data={data.errorPropagation} />
        </Section>
      </div>

      {/* Section 8: Agent Concurrency Timeline */}
      <Section
        number={8}
        title={t("concurrency.title")}
        subtitle={t("concurrency.subtitle")}
        infoKey="concurrency"
      >
        <ConcurrencyTimeline data={data.concurrency} />
      </Section>

      {/* Section 9 + 10: Two Column */}
      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
        <Section
          number={9}
          title={t("complexity.title")}
          subtitle={t("complexity.subtitle")}
          infoKey="complexity"
        >
          <SessionComplexityScatter data={data.complexity} onSessionClick={setSelectedSessionId} />
        </Section>

        <Section
          number={10}
          title={t("compaction.title")}
          subtitle={t("compaction.subtitle")}
          infoKey="compaction"
        >
          <CompactionImpact data={data.compaction} />
        </Section>
      </div>

      {/* Section 11: Session Drill-In */}
      <Section
        number={11}
        title={t("drillIn.title")}
        subtitle={t("drillIn.subtitle")}
        infoKey="drillIn"
      >
        <SessionDrillIn
          sessionId={selectedSessionId}
          onClose={() => setSelectedSessionId(null)}
          onSelectSession={(id) => setSelectedSessionId(id)}
        />
      </Section>
    </div>
  );
}

// ── Section wrapper ──
function Section({
  number,
  title,
  subtitle,
  infoKey,
  children,
}: {
  number: number;
  title: string;
  subtitle: string;
  /** Key under workflows.chartInfo.* - drives the structured popover content. */
  infoKey: string;
  children: React.ReactNode;
}) {
  return (
    // Flex column + a flex-1 card so that, when two Sections share a grid row
    // (lg:grid-cols-2), the grid's default row-stretch reaches the card itself —
    // otherwise a shorter chart (e.g. the Session Complexity Scatter) leaves its
    // card shorter than a taller companion (Compaction Impact) in the same row.
    <div className="flex flex-col h-full">
      <div className="flex items-center justify-between gap-4 mb-3">
        <div className="flex items-center gap-2.5 min-w-0">
          <span className="w-5 h-5 rounded-md bg-accent/15 text-accent text-[11px] font-bold flex items-center justify-center flex-shrink-0">
            {number}
          </span>
          <h2 className="text-sm font-semibold text-fg-primary">{title}</h2>
          <ChartInfoPopover infoKey={infoKey} title={title} />
        </div>
        {/* Quick descriptor; the full explanation lives in the ⓘ popover, so we
            keep this to a single clamped line (ellipsis + hover title) so a long
            translation never wraps and unbalances the header row. */}
        <span
          className="hidden lg:block flex-shrink-0 max-w-[20rem] xl:max-w-sm truncate text-right text-[11px] text-fg-muted"
          title={subtitle}
        >
          {subtitle}
        </span>
      </div>
      <div className="card p-4 flex-1">{children}</div>
    </div>
  );
}

/**
 * Structured info popover for a Workflows chart section. Hover or focus the
 * `i` icon to read three short paragraphs sourced from i18n:
 *
 *   1. What this shows  - what data the chart visualizes
 *   2. How to read it   - visual encoding (axes, sizes, colors, etc.)
 *   3. Why it matters   - what insights the user can extract
 *
 * The popover uses fixed positioning and is clamped to the viewport so it
 * never gets clipped by the sidebar or screen edges. Auto-flips above the
 * trigger when there's no room below.
 */
function ChartInfoPopover({ infoKey, title }: { infoKey: string; title: string }) {
  const { t } = useTranslation("workflows");
  const [open, setOpen] = useState(false);
  const buttonRef = useRef<HTMLButtonElement>(null);
  const popoverRef = useRef<HTMLDivElement>(null);
  const [coords, setCoords] = useState<{ left: number; top: number }>({ left: 0, top: 0 });

  const POPOVER_W = 340;
  const MARGIN = 12;

  useLayoutEffect(() => {
    if (!open) return;
    const update = () => {
      const btn = buttonRef.current;
      const pop = popoverRef.current;
      if (!btn) return;
      const r = btn.getBoundingClientRect();
      const popH = pop?.offsetHeight ?? 280;

      // Center horizontally over the icon, clamp to viewport.
      let left = r.left + r.width / 2 - POPOVER_W / 2;
      if (left < MARGIN) left = MARGIN;
      if (left + POPOVER_W > window.innerWidth - MARGIN) {
        left = window.innerWidth - POPOVER_W - MARGIN;
      }
      // Default below the icon; flip above if not enough room.
      const spaceBelow = window.innerHeight - r.bottom;
      const placeAbove = spaceBelow < popH + MARGIN && r.top > popH + MARGIN;
      const top = placeAbove ? Math.max(MARGIN, r.top - popH - 8) : r.bottom + 8;

      setCoords({ left, top });
    };
    update();
    const raf = requestAnimationFrame(update);
    window.addEventListener("scroll", update, true);
    window.addEventListener("resize", update);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("scroll", update, true);
      window.removeEventListener("resize", update);
    };
  }, [open]);

  useEffect(() => {
    if (!open) return;
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") setOpen(false);
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open]);

  return (
    <>
      <button
        ref={buttonRef}
        type="button"
        aria-label={t("chartInfo.labels.what")}
        aria-expanded={open}
        onMouseEnter={() => setOpen(true)}
        onMouseLeave={() => setOpen(false)}
        onFocus={() => setOpen(true)}
        onBlur={() => setOpen(false)}
        className="flex items-center justify-center rounded-full p-0.5 -m-0.5 text-fg-muted hover:text-fg-secondary transition-colors focus:outline-none focus:ring-1 focus:ring-accent/40"
      >
        <Info className="w-3.5 h-3.5" />
      </button>
      {open && (
        <div
          ref={popoverRef}
          role="tooltip"
          className="fixed z-50 p-3.5 bg-[#12121f] border border-[#2a2a4a] rounded-lg shadow-2xl text-[11px] text-fg-secondary pointer-events-none"
          style={{ left: coords.left, top: coords.top, width: POPOVER_W }}
        >
          <p className="text-xs font-semibold text-fg-primary mb-2.5 pb-2 border-b border-[#2a2a4a]">
            {title}
          </p>

          <p className="font-semibold text-fg-secondary uppercase tracking-wider text-[9px] mb-1">
            {t("chartInfo.labels.what")}
          </p>
          <p className="text-fg-secondary leading-snug mb-2.5">{t(`chartInfo.${infoKey}.what`)}</p>

          <p className="font-semibold text-fg-secondary uppercase tracking-wider text-[9px] mb-1">
            {t("chartInfo.labels.howToRead")}
          </p>
          <p className="text-fg-secondary leading-snug mb-2.5">
            {t(`chartInfo.${infoKey}.howToRead`)}
          </p>

          <p className="font-semibold text-fg-secondary uppercase tracking-wider text-[9px] mb-1">
            {t("chartInfo.labels.why")}
          </p>
          <p className="text-fg-secondary leading-snug">{t(`chartInfo.${infoKey}.why`)}</p>
        </div>
      )}
    </>
  );
}

// ── Page Header ──
function PageHeader({
  statusFilter,
  onStatusFilterChange,
  onRefresh,
  onExport,
  lastUpdated,
}: {
  statusFilter: StatusFilter;
  onStatusFilterChange: (f: StatusFilter) => void;
  onRefresh: () => void;
  onExport: () => void;
  lastUpdated: Date | null;
}) {
  const { t } = useTranslation("workflows");
  const wsConnected = useSyncExternalStore(eventBus.onConnection, () => eventBus.connected);
  const filters: { value: StatusFilter; label: string }[] = [
    { value: "all", label: t("allSessions") },
    { value: "active", label: t("activeOnly") },
    { value: "completed", label: t("completed") },
  ];

  return (
    <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
      <div className="flex items-center gap-3">
        <div className="w-9 h-9 rounded-xl bg-accent/15 flex items-center justify-center">
          <Workflow className="w-4.5 h-4.5 text-accent" />
        </div>
        <div>
          <div className="flex items-center gap-2">
            <h1 className="text-lg font-semibold text-fg-primary">{t("title")}</h1>
            {wsConnected ? (
              <span className="flex items-center gap-1.5 text-[11px] text-status-success bg-status-success/10 border border-status-success/20 px-2 py-0.5 rounded-full">
                <span className="w-1.5 h-1.5 rounded-full bg-status-success animate-pulse-dot" />
                {t("common:live")}
              </span>
            ) : (
              <span className="flex items-center gap-1.5 text-[11px] text-fg-secondary bg-surface-4/10 border border-border-light/20 px-2 py-0.5 rounded-full">
                <span className="w-1.5 h-1.5 rounded-full bg-surface-4" />
                {t("common:offline")}
              </span>
            )}
          </div>
          <p className="text-xs text-fg-muted">{t("subtitle")}</p>
        </div>
      </div>

      <div className="flex items-center gap-2 flex-wrap">
        {/* Status filter tabs */}
        <div className="flex bg-surface-2 rounded-lg p-0.5 border border-border">
          {filters.map((f) => (
            <button
              key={f.value}
              onClick={() => onStatusFilterChange(f.value)}
              className={`px-3 py-1.5 rounded-md text-xs font-medium transition-colors ${
                statusFilter === f.value
                  ? "bg-accent/15 text-accent"
                  : "text-fg-muted hover:text-fg-secondary"
              }`}
            >
              {f.label}
            </button>
          ))}
        </div>

        {/* Actions */}
        <button
          onClick={onRefresh}
          className="p-2 rounded-lg text-fg-muted hover:text-fg-secondary hover:bg-surface-3 transition-colors"
          title={t("refreshData")}
        >
          <RefreshCw className="w-4 h-4" />
        </button>
        <button
          onClick={onExport}
          className="p-2 rounded-lg text-fg-muted hover:text-fg-secondary hover:bg-surface-3 transition-colors"
          title={t("exportJson")}
        >
          <Download className="w-4 h-4" />
        </button>

        {lastUpdated && (
          <span className="text-[10px] text-fg-muted ml-1">
            {t("common:updated")}
            {lastUpdated.toLocaleTimeString()}
          </span>
        )}
      </div>
    </div>
  );
}
