"use client";

import Link from "next/link";
import { useMemo, useState, type ReactNode } from "react";
import ReactMarkdown from "react-markdown";
import type {
  AlertEvent,
  ClassifierHitEvent,
  FlowPacket,
  IslandflowAiCompiledScreen,
  IslandflowAiPlanType,
  IslandflowAiRateLimitSnapshot,
  IslandflowAiReasoningEffort,
  IslandflowAiTaskKind,
  IslandflowAiTaskSnapshot,
  IslandflowAiTaskStatus,
  OptionFlowFilters,
  OptionPrint,
  SmartMoneyEvent,
} from "@islandflow/types";
import { useDesktopAi } from "./desktop-ai";

const numberFormatter = new Intl.NumberFormat("en-US");
const usdFormatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  minimumFractionDigits: 2,
  maximumFractionDigits: 4,
});

const humanizeValue = (value: string | null | undefined): string => {
  if (!value) {
    return "Unknown";
  }
  return value
    .replace(/_/g, " ")
    .replace(/\b\w/g, (char) => char.toUpperCase());
};

const formatTokens = (value: number): string => numberFormatter.format(value);

const formatUsd = (value: number | null): string =>
  value === null ? "Unavailable" : usdFormatter.format(value);

const formatTimestamp = (value: number | null): string => {
  if (!value) {
    return "Not reported";
  }
  return new Intl.DateTimeFormat("en-US", {
    dateStyle: "medium",
    timeStyle: "short",
  }).format(value);
};

const formatPercent = (value: number): string => `${Math.round(value)}%`;

const getTaskStatusLabel = (value: string): string => humanizeValue(value);

type CopilotTaskCache = Record<string, string>;

const RUNNING_TASK_STATUSES: IslandflowAiTaskStatus[] = ["queued", "running"];

export const createCopilotTaskCacheKey = (
  kind: IslandflowAiTaskKind,
  contextKey: string,
): string => `${kind}:${contextKey}`;

export const getCachedCopilotTaskId = (
  cache: CopilotTaskCache,
  kind: IslandflowAiTaskKind,
  contextKey: string,
): string | null => cache[createCopilotTaskCacheKey(kind, contextKey)] ?? null;

export const shouldShowCopilotRegenerate = (
  task: Pick<IslandflowAiTaskSnapshot, "status" | "text" | "compiledScreen"> | null,
): boolean =>
  Boolean(
    task &&
      task.status === "completed" &&
      (task.text.trim().length > 0 || task.compiledScreen),
  );

export const isCopilotTaskRunning = (
  task: Pick<IslandflowAiTaskSnapshot, "status"> | null,
): boolean => Boolean(task && RUNNING_TASK_STATUSES.includes(task.status));

export const getCopilotTaskSurfaceState = (
  task: Pick<IslandflowAiTaskSnapshot, "status" | "text" | "compiledScreen" | "error"> | null,
): "empty" | "running" | "completed" | "failed" | "cancelled" => {
  if (!task) {
    return "empty";
  }
  if (RUNNING_TASK_STATUSES.includes(task.status)) {
    return "running";
  }
  if (task.status === "completed") {
    return "completed";
  }
  if (task.status === "failed") {
    return "failed";
  }
  return "cancelled";
};

const getSmartMoneyContextKey = (event: SmartMoneyEvent): string =>
  [event.underlying_id, event.source_ts, event.event_id].join(":");

type DesktopAiSettingsNotice = {
  title: string;
  body: string;
};

export const getDesktopAiSettingsBridgeNotice = (
  shellAvailable: boolean,
  bridgeAvailable: boolean,
): DesktopAiSettingsNotice | null => {
  if (!shellAvailable) {
    return {
      title: "Desktop app required",
      body: "Open Islandflow Desktop to connect ChatGPT, load managed models, and use native Copilot controls.",
    };
  }

  if (!bridgeAvailable) {
    return {
      title: "Bridge unavailable in this window",
      body: "This Islandflow Desktop window is missing its native AI bridge, so login actions and model controls stay disabled until the bridge reconnects. Reload the window or restart Islandflow if this keeps happening.",
    };
  }

  return null;
};

export const getDesktopAiModelSelectLabel = (
  shellAvailable: boolean,
  bridgeAvailable: boolean,
  loggedIn: boolean,
  modelCount: number,
): string => {
  if (!shellAvailable) {
    return "Desktop app required";
  }
  if (!bridgeAvailable) {
    return "Bridge unavailable";
  }
  if (!loggedIn) {
    return "Connect ChatGPT to load models";
  }
  if (modelCount === 0) {
    return "Waiting for managed models";
  }
  return "Use server default";
};

export const getDesktopAiModelListEmptyCopy = (
  shellAvailable: boolean,
  bridgeAvailable: boolean,
  loggedIn: boolean,
): string => {
  if (!shellAvailable) {
    return "Open Islandflow Desktop to load managed model options.";
  }
  if (!bridgeAvailable) {
    return "Managed model options will appear here after the native AI bridge reconnects.";
  }
  if (!loggedIn) {
    return "Connect a ChatGPT or Codex account to load the managed model catalog.";
  }
  return "No model catalog has been reported yet.";
};

export const getDesktopAiProfileBadgeLabel = (
  selected: boolean,
  statusLabel: string,
  bridgeAvailable: boolean,
): string => {
  if (!bridgeAvailable) {
    return statusLabel;
  }
  return selected ? "Selected" : statusLabel;
};

const findTask = <T extends { taskId: string }>(
  tasks: T[],
  taskId: string | null,
): T | null => {
  if (!taskId) {
    return null;
  }
  return tasks.find((task) => task.taskId === taskId) ?? null;
};

const getCompiledScreenSummary = (
  compiled: IslandflowAiCompiledScreen,
): string[] => {
  const filters = compiled.compiledFilters;
  if (!filters) {
    return [];
  }

  const parts: string[] = [];
  if (filters.view) {
    parts.push(`View: ${filters.view}`);
  }
  if (filters.securityTypes?.length) {
    parts.push(`Security: ${filters.securityTypes.join(", ")}`);
  }
  if (filters.optionTypes?.length) {
    parts.push(`Options: ${filters.optionTypes.join(", ")}`);
  }
  if (filters.nbboSides?.length) {
    parts.push(`NBBO: ${filters.nbboSides.join(", ")}`);
  }
  if (typeof filters.minNotional === "number") {
    parts.push(`Min notional: $${numberFormatter.format(filters.minNotional)}`);
  }

  return parts;
};

const CopilotPane = ({
  title,
  eyebrow,
  actions,
  wide = false,
  children,
}: {
  title: string;
  eyebrow?: string;
  actions?: ReactNode;
  wide?: boolean;
  children: ReactNode;
}) => {
  return (
    <section
      className={`terminal-pane copilot-pane${wide ? " copilot-pane-wide" : ""}`}
    >
      <div className="terminal-pane-head">
        <div className="terminal-pane-title-row">
          <div>
            {eyebrow ? <div className="copilot-kicker">{eyebrow}</div> : null}
            <h2 className="terminal-pane-title">{title}</h2>
          </div>
        </div>
        {actions ? (
          <div className="terminal-pane-actions">{actions}</div>
        ) : null}
      </div>
      <div className="terminal-pane-body copilot-pane-body">{children}</div>
    </section>
  );
};

const UsageBreakdown = ({
  title,
  breakdown,
  normalizedCostUsd,
  turnCount,
  activeDays,
}: {
  title: string;
  breakdown: {
    totalTokens: number;
    inputTokens: number;
    cachedInputTokens: number;
    outputTokens: number;
    reasoningOutputTokens: number;
  };
  normalizedCostUsd: number | null;
  turnCount: number;
  activeDays: number;
}) => {
  return (
    <div className="copilot-usage-block">
      <div className="copilot-usage-title-row">
        <h3>{title}</h3>
        <span className="copilot-usage-cost">
          {formatUsd(normalizedCostUsd)}
        </span>
      </div>
      <div className="copilot-token-grid">
        <div className="copilot-token-row">
          <span>Total tokens</span>
          <strong>{formatTokens(breakdown.totalTokens)}</strong>
        </div>
        <div className="copilot-token-row">
          <span>Input</span>
          <strong>{formatTokens(breakdown.inputTokens)}</strong>
        </div>
        <div className="copilot-token-row">
          <span>Cached input</span>
          <strong>{formatTokens(breakdown.cachedInputTokens)}</strong>
        </div>
        <div className="copilot-token-row">
          <span>Output</span>
          <strong>{formatTokens(breakdown.outputTokens)}</strong>
        </div>
        <div className="copilot-token-row">
          <span>Reasoning</span>
          <strong>{formatTokens(breakdown.reasoningOutputTokens)}</strong>
        </div>
        <div className="copilot-token-row">
          <span>Turns</span>
          <strong>{formatTokens(turnCount)}</strong>
        </div>
        <div className="copilot-token-row">
          <span>Active days</span>
          <strong>{formatTokens(activeDays)}</strong>
        </div>
      </div>
    </div>
  );
};

const RateLimitBoard = ({
  limit,
}: {
  limit: IslandflowAiRateLimitSnapshot;
}) => {
  return (
    <div
      className="copilot-limit-card"
      key={limit.limitId ?? limit.limitName ?? "default"}
    >
      <div className="copilot-limit-head">
        <div>
          <strong>{limit.limitName ?? "Default rate window"}</strong>
          <p className="copilot-note">
            {limit.planType
              ? `Plan ${humanizeValue(limit.planType)}`
              : "Plan not reported"}
          </p>
        </div>
        {limit.reachedType ? (
          <span className="copilot-badge warning">
            {humanizeValue(limit.reachedType)}
          </span>
        ) : null}
      </div>
      <div className="copilot-limit-grid">
        {limit.primary ? (
          <div className="copilot-limit-window">
            <span>Primary</span>
            <strong>{formatPercent(limit.primary.usedPercent)}</strong>
            <p className="copilot-note">
              Resets {formatTimestamp(limit.primary.resetsAt)}
            </p>
          </div>
        ) : null}
        {limit.secondary ? (
          <div className="copilot-limit-window">
            <span>Secondary</span>
            <strong>{formatPercent(limit.secondary.usedPercent)}</strong>
            <p className="copilot-note">
              Resets {formatTimestamp(limit.secondary.resetsAt)}
            </p>
          </div>
        ) : null}
      </div>
      {limit.creditsBalance || limit.unlimitedCredits !== null ? (
        <p className="copilot-note">
          Credits:{" "}
          {limit.unlimitedCredits
            ? "unlimited"
            : limit.creditsBalance
              ? limit.creditsBalance
              : limit.hasCredits === false
                ? "none"
                : "not reported"}
        </p>
      ) : null}
    </div>
  );
};

const TaskOutput = ({
  taskId,
  emptyMessage,
  onCancel,
}: {
  taskId: string | null;
  emptyMessage: string;
  onCancel?: (taskId: string) => void;
}) => {
  const { state } = useDesktopAi();
  const task = findTask(state.tasks, taskId);
  const surfaceState = getCopilotTaskSurfaceState(task);

  if (!task) {
    return (
      <div className="copilot-task-output copilot-task-output-empty">
        <p className="copilot-empty">{emptyMessage}</p>
      </div>
    );
  }

  return (
    <div className={`copilot-task-output state-${surfaceState}`} aria-live="polite">
      <div className="copilot-task-head">
        <div>
          <strong>{task.title}</strong>
          <p className="copilot-note">
            {task.subtitle} · {getTaskStatusLabel(task.status)}
          </p>
        </div>
        <span className={`copilot-badge status-${task.status}`}>
          {getTaskStatusLabel(task.status)}
        </span>
      </div>
      {surfaceState === "running" ? (
        <div className="copilot-task-running-row">
          <span className="copilot-spinner" aria-hidden="true" />
          <p className="copilot-note">
            {task.status === "queued"
              ? "Queued with the desktop Copilot bridge."
              : "Generating analysis as deltas arrive."}
          </p>
          {onCancel ? (
            <button
              className="terminal-button"
              type="button"
              onClick={() => onCancel(task.taskId)}
            >
              Cancel
            </button>
          ) : null}
        </div>
      ) : null}
      {task.error ? <p className="copilot-error">{task.error}</p> : null}
      {task.status === "cancelled" && !task.text ? (
        <p className="copilot-note">This Copilot task was cancelled locally.</p>
      ) : null}
      {task.text ? (
        <div className="copilot-markdown">
          <ReactMarkdown>{task.text}</ReactMarkdown>
        </div>
      ) : null}
      {task.compiledScreen ? (
        <CompiledScreenResult compiled={task.compiledScreen} />
      ) : null}
    </div>
  );
};

const CompiledScreenResult = ({
  compiled,
}: {
  compiled: IslandflowAiCompiledScreen;
}) => {
  const summary = getCompiledScreenSummary(compiled);

  return (
    <div className="copilot-compiled-screen">
      {summary.length > 0 ? (
        <div className="copilot-chip-row">
          {summary.map((item) => (
            <span className="copilot-chip" key={item}>
              {item}
            </span>
          ))}
        </div>
      ) : (
        <p className="copilot-note">
          No filter fields were compiled from this prompt.
        </p>
      )}
      {compiled.unhandledClauses.length > 0 ? (
        <div className="copilot-unhandled-list">
          <div className="copilot-list-title">Unhandled clauses</div>
          {compiled.unhandledClauses.map((item) => (
            <div className="copilot-inline-row" key={item}>
              <span>{item}</span>
            </div>
          ))}
        </div>
      ) : null}
    </div>
  );
};

const AccountSummary = ({
  loggedIn,
  email,
  planType,
}: {
  loggedIn: boolean;
  email: string | null;
  planType: IslandflowAiPlanType | null;
}) => {
  return (
    <div className="copilot-hero">
      <div>
        <p className="copilot-kicker">Desktop-only official Codex bridge</p>
        <h1 className="page-title">Analyst Copilot</h1>
        <p className="copilot-hero-copy">
          Managed ChatGPT login stays user-scoped, deterministic smart-money
          classification stays in charge, and every AI turn is tracked with
          exact token telemetry from the app-server.
        </p>
      </div>
      <div className="copilot-hero-meta">
        <div className="copilot-stat">
          <span>Account</span>
          <strong>{loggedIn ? (email ?? "Connected") : "Disconnected"}</strong>
        </div>
        <div className="copilot-stat">
          <span>Plan</span>
          <strong>
            {loggedIn ? humanizeValue(planType) : "Not connected"}
          </strong>
        </div>
      </div>
    </div>
  );
};

const LoginStatePanel = () => {
  const {
    bridgeAvailable,
    shellAvailable,
    state,
    loginWithBrowser,
    loginWithDeviceCode,
    cancelLogin,
    logout,
  } = useDesktopAi();
  const [busyAction, setBusyAction] = useState<string | null>(null);
  const [actionError, setActionError] = useState<string | null>(null);
  const loginState = state.account.login;
  const actionsDisabled = busyAction !== null || !bridgeAvailable;
  const bridgeNotice = getDesktopAiSettingsBridgeNotice(
    shellAvailable,
    bridgeAvailable,
  );

  const runAction = async (label: string, action: () => Promise<void>) => {
    setBusyAction(label);
    setActionError(null);
    try {
      await action();
    } catch (error) {
      setActionError(error instanceof Error ? error.message : String(error));
    } finally {
      setBusyAction(null);
    }
  };

  return (
    <CopilotPane
      title="Account and access"
      eyebrow="Managed auth"
      wide
      actions={
        <>
          {state.account.loggedIn ? (
            <button
              className="terminal-button"
              type="button"
              onClick={() => void runAction("logout", logout)}
              disabled={actionsDisabled}
            >
              {busyAction === "logout" ? "Logging out" : "Logout"}
            </button>
          ) : bridgeNotice ? (
            shellAvailable ? (
              <button
                className="terminal-button terminal-button-primary"
                type="button"
                onClick={() => window.location.reload()}
              >
                Reload window
              </button>
            ) : null
          ) : (
            <>
              <button
                className="terminal-button terminal-button-primary"
                type="button"
                onClick={() => void runAction("browser", loginWithBrowser)}
                disabled={actionsDisabled}
              >
                {busyAction === "browser" ? "Opening browser" : "Browser login"}
              </button>
              <button
                className="terminal-button"
                type="button"
                onClick={() => void runAction("device", loginWithDeviceCode)}
                disabled={actionsDisabled}
              >
                {busyAction === "device" ? "Preparing code" : "Device code"}
              </button>
            </>
          )}
          {(loginState.status === "browser_pending" ||
            loginState.status === "device_code_pending") &&
          !state.account.loggedIn ? (
            <button
              className="terminal-button"
              type="button"
              onClick={() => void runAction("cancel", cancelLogin)}
              disabled={actionsDisabled}
            >
              Cancel
            </button>
          ) : null}
        </>
      }
    >
      <AccountSummary
        loggedIn={state.account.loggedIn}
        email={state.account.email}
        planType={state.account.planType}
      />
      {bridgeNotice ? (
        <div className="copilot-callout copilot-callout-warning">
          <strong>{bridgeNotice.title}</strong>
          <p className="copilot-note">{bridgeNotice.body}</p>
        </div>
      ) : null}
      <div className="copilot-account-grid">
        <div className="copilot-account-card">
          <div className="copilot-list-title">Profile slots</div>
          {state.profiles.map((profile) => (
            <div className="copilot-inline-row" key={profile.id}>
              <div>
                <strong>{profile.label}</strong>
                <p className="copilot-note">{profile.description}</p>
              </div>
              <span
                className={`copilot-badge${profile.enabled ? "" : " muted"}`}
              >
                {getDesktopAiProfileBadgeLabel(
                  profile.selected,
                  profile.statusLabel,
                  bridgeAvailable,
                )}
              </span>
            </div>
          ))}
        </div>
        <div className="copilot-account-card">
          <div className="copilot-list-title">Session status</div>
          <div className="copilot-inline-row">
            <span>Transport</span>
            <strong>{humanizeValue(state.transportStatus)}</strong>
          </div>
          <div className="copilot-inline-row">
            <span>Auth mode</span>
            <strong>{humanizeValue(state.account.authMode)}</strong>
          </div>
          <div className="copilot-inline-row">
            <span>OpenAI auth required</span>
            <strong>{state.account.requiresOpenaiAuth ? "Yes" : "No"}</strong>
          </div>
          {state.transportError ? (
            <p className="copilot-error">{state.transportError}</p>
          ) : null}
          {loginState.message ? (
            <p className="copilot-note">{loginState.message}</p>
          ) : null}
          {loginState.status === "browser_pending" ? (
            <div className="copilot-callout">
              <strong>Browser login in progress</strong>
              <p className="copilot-note">
                Finish the ChatGPT sign-in flow in your browser. Islandflow will
                update automatically.
              </p>
            </div>
          ) : null}
          {loginState.status === "device_code_pending" ? (
            <div className="copilot-callout">
              <strong>Device code</strong>
              <pre className="copilot-device-code">{loginState.userCode}</pre>
              <p className="copilot-note">
                Visit {loginState.verificationUrl} in any browser and enter the
                code above.
              </p>
            </div>
          ) : null}
          {actionError ? <p className="copilot-error">{actionError}</p> : null}
        </div>
      </div>
    </CopilotPane>
  );
};

export function DesktopAiSettingsRoute() {
  const { bridgeAvailable, shellAvailable, state, updatePreferences } =
    useDesktopAi();
  const [busyPreference, setBusyPreference] = useState<
    "model" | "reasoning" | null
  >(null);
  const [preferenceError, setPreferenceError] = useState<string | null>(null);
  const rateLimits = Object.values(state.rateLimitsByLimitId);
  const selectedModel = state.preferences.model ?? "";
  const selectedReasoning = state.preferences.reasoningEffort ?? "";
  const modelControlsNotice = getDesktopAiSettingsBridgeNotice(
    shellAvailable,
    bridgeAvailable,
  );
  const modelSelectLabel = getDesktopAiModelSelectLabel(
    shellAvailable,
    bridgeAvailable,
    state.account.loggedIn,
    state.models.length,
  );
  const modelListEmptyCopy = getDesktopAiModelListEmptyCopy(
    shellAvailable,
    bridgeAvailable,
    state.account.loggedIn,
  );

  const savePreference = async (
    key: "model" | "reasoning",
    next: Partial<{
      model: string | null;
      reasoningEffort: IslandflowAiReasoningEffort | null;
    }>,
  ) => {
    setBusyPreference(key);
    setPreferenceError(null);
    try {
      await updatePreferences(next);
    } catch (error) {
      setPreferenceError(
        error instanceof Error ? error.message : String(error),
      );
    } finally {
      setBusyPreference(null);
    }
  };

  return (
    <div className="page-shell">
      {!shellAvailable ? (
        <CopilotPane
          title="Desktop required"
          eyebrow="Browser-only fallback"
          wide
        >
          <div className="copilot-unavailable">
            <p>
              AI controls are intentionally read-only in the browser build. Open
              Islandflow Desktop to use managed ChatGPT login, structured
              Copilot turns, and app-server token telemetry.
            </p>
          </div>
        </CopilotPane>
      ) : null}

      <LoginStatePanel />

      <div className="page-grid page-grid-settings">
        <CopilotPane title="Model controls" eyebrow="Execution">
          {modelControlsNotice ? (
            <div className="copilot-callout copilot-callout-warning">
              <strong>{modelControlsNotice.title}</strong>
              <p className="copilot-note">{modelControlsNotice.body}</p>
            </div>
          ) : state.models.length === 0 ? (
            <div className="copilot-callout">
              <strong>Managed models are not loaded yet</strong>
              <p className="copilot-note">{modelListEmptyCopy}</p>
            </div>
          ) : null}
          <div className="copilot-field-grid">
            <label className="copilot-field">
              <span className="copilot-field-label">Model</span>
              <select
                className="copilot-select"
                value={selectedModel}
                onChange={(event) =>
                  void savePreference("model", {
                    model: event.target.value.trim()
                      ? event.target.value
                      : null,
                  })
                }
                disabled={
                  busyPreference !== null ||
                  state.models.length === 0 ||
                  !bridgeAvailable
                }
              >
                <option value="">{modelSelectLabel}</option>
                {state.models.map((model) => (
                  <option key={model.id} value={model.model}>
                    {model.displayName}
                  </option>
                ))}
              </select>
            </label>
            <label className="copilot-field">
              <span className="copilot-field-label">Reasoning</span>
              <select
                className="copilot-select"
                value={selectedReasoning}
                onChange={(event) =>
                  void savePreference("reasoning", {
                    reasoningEffort: event.target.value.trim()
                      ? (event.target.value as IslandflowAiReasoningEffort)
                      : null,
                  })
                }
                disabled={busyPreference !== null || !bridgeAvailable}
              >
                <option value="">Use model default</option>
                <option value="none">None</option>
                <option value="minimal">Minimal</option>
                <option value="low">Low</option>
                <option value="medium">Medium</option>
                <option value="high">High</option>
                <option value="xhigh">XHigh</option>
              </select>
            </label>
          </div>
          <div className="copilot-model-list">
            {state.models.length === 0 ? (
              <p className="copilot-empty">{modelListEmptyCopy}</p>
            ) : (
              state.models.map((model) => (
                <div className="copilot-model-row" key={model.id}>
                  <div>
                    <strong>{model.displayName}</strong>
                    <p className="copilot-note">{model.description}</p>
                  </div>
                  <div className="copilot-model-meta">
                    <span>{model.model}</span>
                    {model.pricing ? (
                      <span>
                        {formatUsd(model.pricing.inputUsdPer1MTokens)} / 1M
                        input
                      </span>
                    ) : null}
                  </div>
                </div>
              ))
            )}
          </div>
          {state.models.find((model) => model.model === state.preferences.model)
            ?.pricing ? (
            <p className="copilot-note">
              Normalized estimates use current API pricing for the selected
              model, not your literal ChatGPT subscription bill.
            </p>
          ) : null}
          {preferenceError ? (
            <p className="copilot-error">{preferenceError}</p>
          ) : null}
        </CopilotPane>

        <CopilotPane title="Rate limits" eyebrow="Live windows">
          {rateLimits.length === 0 ? (
            <p className="copilot-empty">
              No rate-limit snapshots have been reported yet.
            </p>
          ) : (
            <div className="copilot-limit-list">
              {rateLimits.map((limit) => (
                <RateLimitBoard
                  key={limit.limitId ?? limit.limitName ?? "default"}
                  limit={limit}
                />
              ))}
            </div>
          )}
        </CopilotPane>

        <CopilotPane
          title="Usage dashboard"
          eyebrow="Exact app-server telemetry"
          wide
        >
          <div className="copilot-usage-grid">
            <UsageBreakdown
              title="Today"
              breakdown={state.usage.today.breakdown}
              normalizedCostUsd={state.usage.today.normalizedCostUsd}
              turnCount={state.usage.today.turnCount}
              activeDays={state.usage.today.activeDays}
            />
            <UsageBreakdown
              title="Lifetime"
              breakdown={state.usage.lifetime.breakdown}
              normalizedCostUsd={state.usage.lifetime.normalizedCostUsd}
              turnCount={state.usage.lifetime.turnCount}
              activeDays={state.usage.lifetime.activeDays}
            />
          </div>
        </CopilotPane>

        <CopilotPane title="Recent turns" eyebrow="Per-thread usage">
          {state.usage.recentTurns.length === 0 ? (
            <p className="copilot-empty">No tracked turns yet.</p>
          ) : (
            <div className="copilot-turn-list">
              {state.usage.recentTurns.map((turn) => (
                <div
                  className="copilot-turn-row"
                  key={`${turn.threadId}:${turn.turnId}`}
                >
                  <div>
                    <strong>{turn.taskTitle ?? "Ad hoc turn"}</strong>
                    <p className="copilot-note">
                      {turn.model ?? "default"} ·{" "}
                      {formatTimestamp(turn.updatedAt)}
                    </p>
                  </div>
                  <div className="copilot-turn-metrics">
                    <span>{formatTokens(turn.breakdown.totalTokens)} tok</span>
                    <span>{formatUsd(turn.normalizedCostUsd)}</span>
                  </div>
                </div>
              ))}
            </div>
          )}
        </CopilotPane>

        <CopilotPane title="Recent analyses" eyebrow="Task feed">
          {state.tasks.length === 0 ? (
            <p className="copilot-empty">No Copilot tasks have been run yet.</p>
          ) : (
            <div className="copilot-task-list">
              {state.tasks.map((task) => (
                <div className="copilot-task-list-row" key={task.taskId}>
                  <div>
                    <strong>{task.title}</strong>
                    <p className="copilot-note">
                      {task.subtitle} · {humanizeValue(task.model)}
                    </p>
                  </div>
                  <span className={`copilot-badge status-${task.status}`}>
                    {getTaskStatusLabel(task.status)}
                  </span>
                </div>
              ))}
            </div>
          )}
        </CopilotPane>
      </div>
    </div>
  );
}

export const requireDesktopActionCopy = (
  shellAvailable: boolean,
  bridgeAvailable: boolean,
  loggedIn: boolean,
): string => {
  if (!shellAvailable) {
    return "This control is desktop-only. Open Islandflow Desktop to run Copilot tasks.";
  }
  if (!bridgeAvailable) {
    return "Islandflow Desktop is open, but this window is missing the native AI bridge. Reload the window or restart the app.";
  }
  if (!loggedIn) {
    return "Connect a ChatGPT or Codex account in Settings before running Copilot analysis.";
  }
  return "";
};

const SmartMoneyTaskButton = ({
  label,
  kind,
  symbol,
  disabled,
  busyKind,
  selected,
  cached,
  onRun,
}: {
  label: string;
  kind: IslandflowAiTaskKind;
  symbol: string;
  disabled: boolean;
  busyKind: IslandflowAiTaskKind | null;
  selected: boolean;
  cached: boolean;
  onRun: (kind: IslandflowAiTaskKind) => void;
}) => {
  return (
    <button
      className={`terminal-button${kind === "smart-money-explain" ? " terminal-button-primary" : ""}${selected ? " is-active" : ""}`}
      type="button"
      onClick={() => onRun(kind)}
      disabled={busyKind !== null || disabled}
      title={`${label} for ${symbol}`}
    >
      {busyKind === kind ? "Running" : cached ? `${label} cached` : label}
    </button>
  );
};

export function SmartMoneyCopilotPanel({
  event,
  flowPacket,
  evidencePrints,
  relatedPackets,
}: {
  event: SmartMoneyEvent;
  flowPacket: FlowPacket | null;
  evidencePrints: OptionPrint[];
  relatedPackets: FlowPacket[];
}) {
  const { bridgeAvailable, shellAvailable, state, runTask, cancelTask } = useDesktopAi();
  const [busyKind, setBusyKind] = useState<IslandflowAiTaskKind | null>(null);
  const [selectedKind, setSelectedKind] = useState<IslandflowAiTaskKind>("smart-money-explain");
  const [activeTaskId, setActiveTaskId] = useState<string | null>(null);
  const [taskCache, setTaskCache] = useState<CopilotTaskCache>({});
  const [taskError, setTaskError] = useState<string | null>(null);
  const contextKey = useMemo(() => getSmartMoneyContextKey(event), [event]);
  const activeTask = useMemo(
    () => findTask(state.tasks, activeTaskId),
    [state.tasks, activeTaskId],
  );
  const disabledCopy = requireDesktopActionCopy(
    shellAvailable,
    bridgeAvailable,
    state.account.loggedIn,
  );
  const actionsDisabled = !bridgeAvailable || !state.account.loggedIn;

  const handleRun = async (kind: IslandflowAiTaskKind, options?: { regenerate?: boolean }) => {
    setSelectedKind(kind);
    const cachedTaskId = getCachedCopilotTaskId(taskCache, kind, contextKey);
    const cachedTask = findTask(state.tasks, cachedTaskId);
    if (!options?.regenerate && cachedTask) {
      setActiveTaskId(cachedTask.taskId);
      setTaskError(null);
      return;
    }

    setBusyKind(kind);
    setTaskError(null);
    try {
      const result = await runTask({
        kind: kind as
          | "smart-money-explain"
          | "smart-money-skeptic"
          | "smart-money-burst-summary"
          | "watchlist-synthesis",
        context: {
          event,
          flowPacket,
          evidencePrints,
          relatedPackets,
        },
      });
      setActiveTaskId(result.taskId);
      setTaskCache((current) => ({
        ...current,
        [createCopilotTaskCacheKey(kind, contextKey)]: result.taskId,
      }));
    } catch (error) {
      setTaskError(error instanceof Error ? error.message : String(error));
    } finally {
      setBusyKind(null);
    }
  };

  const handleCancel = async (taskId: string) => {
    setTaskError(null);
    try {
      await cancelTask(taskId);
    } catch (error) {
      setTaskError(error instanceof Error ? error.message : String(error));
    }
  };

  const canRegenerate = shouldShowCopilotRegenerate(activeTask);

  return (
    <div className="copilot-inline-panel">
      <div className="copilot-inline-head">
        <div>
          <div className="copilot-list-title">Analyst Copilot</div>
          <p className="copilot-note">
            Structured interpretation only, the deterministic classifier remains
            the source of truth.
          </p>
        </div>
        <Link className="terminal-button" href="/settings">
          AI settings
        </Link>
      </div>
      <div className="copilot-action-grid">
        <SmartMoneyTaskButton
          label="Explain"
          kind="smart-money-explain"
          symbol={event.underlying_id}
          disabled={actionsDisabled}
          busyKind={busyKind}
          selected={selectedKind === "smart-money-explain"}
          cached={Boolean(getCachedCopilotTaskId(taskCache, "smart-money-explain", contextKey))}
          onRun={(kind) => void handleRun(kind)}
        />
        <SmartMoneyTaskButton
          label="Counter-thesis"
          kind="smart-money-skeptic"
          symbol={event.underlying_id}
          disabled={actionsDisabled}
          busyKind={busyKind}
          selected={selectedKind === "smart-money-skeptic"}
          cached={Boolean(getCachedCopilotTaskId(taskCache, "smart-money-skeptic", contextKey))}
          onRun={(kind) => void handleRun(kind)}
        />
        <SmartMoneyTaskButton
          label="Burst summary"
          kind="smart-money-burst-summary"
          symbol={event.underlying_id}
          disabled={actionsDisabled}
          busyKind={busyKind}
          selected={selectedKind === "smart-money-burst-summary"}
          cached={Boolean(getCachedCopilotTaskId(taskCache, "smart-money-burst-summary", contextKey))}
          onRun={(kind) => void handleRun(kind)}
        />
        <SmartMoneyTaskButton
          label="Watchlist"
          kind="watchlist-synthesis"
          symbol={event.underlying_id}
          disabled={actionsDisabled}
          busyKind={busyKind}
          selected={selectedKind === "watchlist-synthesis"}
          cached={Boolean(getCachedCopilotTaskId(taskCache, "watchlist-synthesis", contextKey))}
          onRun={(kind) => void handleRun(kind)}
        />
      </div>
      {canRegenerate ? (
        <div className="copilot-apply-row">
          <button
            className="terminal-button"
            type="button"
            onClick={() => void handleRun(selectedKind, { regenerate: true })}
            disabled={actionsDisabled || busyKind !== null}
          >
            Regenerate
          </button>
        </div>
      ) : null}
      {disabledCopy ? <p className="copilot-note">{disabledCopy}</p> : null}
      {taskError ? <p className="copilot-error">{taskError}</p> : null}
      <TaskOutput
        taskId={activeTaskId}
        emptyMessage="Run an explanation, skepticism pass, burst summary, or watchlist synthesis to see the result here."
        onCancel={(taskId) => void handleCancel(taskId)}
      />
    </div>
  );
}

export function ReplayCopilotPanel({
  ticker,
  flowFilters,
  alerts,
  smartMoneyEvents,
  classifierHits,
  flowPackets,
  optionPrints,
}: {
  ticker: string | null;
  flowFilters: OptionFlowFilters;
  alerts: AlertEvent[];
  smartMoneyEvents: SmartMoneyEvent[];
  classifierHits: ClassifierHitEvent[];
  flowPackets: FlowPacket[];
  optionPrints: OptionPrint[];
}) {
  const { bridgeAvailable, shellAvailable, state, runTask, cancelTask } = useDesktopAi();
  const [activeTaskId, setActiveTaskId] = useState<string | null>(null);
  const [busy, setBusy] = useState(false);
  const [taskError, setTaskError] = useState<string | null>(null);
  const activeTask = useMemo(
    () => findTask(state.tasks, activeTaskId),
    [state.tasks, activeTaskId],
  );
  const disabledCopy = requireDesktopActionCopy(
    shellAvailable,
    bridgeAvailable,
    state.account.loggedIn,
  );
  const actionsDisabled = busy || !bridgeAvailable || !state.account.loggedIn;

  const handleRun = async () => {
    setBusy(true);
    setTaskError(null);
    try {
      const result = await runTask({
        kind: "replay-postmortem",
        context: {
          ticker,
          flowFilters,
          alerts,
          smartMoneyEvents,
          classifierHits,
          flowPackets,
          optionPrints,
        },
      });
      setActiveTaskId(result.taskId);
    } catch (error) {
      setTaskError(error instanceof Error ? error.message : String(error));
    } finally {
      setBusy(false);
    }
  };

  const handleCancel = async (taskId: string) => {
    setTaskError(null);
    try {
      await cancelTask(taskId);
    } catch (error) {
      setTaskError(error instanceof Error ? error.message : String(error));
    }
  };

  const canRegenerate = shouldShowCopilotRegenerate(activeTask);

  return (
    <CopilotPane
      title="Replay postmortem"
      eyebrow="Structured recap"
      actions={
        <>
          <Link className="terminal-button" href="/settings">
            AI settings
          </Link>
          <button
            className="terminal-button terminal-button-primary"
            type="button"
            onClick={() => void handleRun()}
            disabled={actionsDisabled}
          >
            {busy ? "Running" : canRegenerate ? "Regenerate" : "Generate postmortem"}
          </button>
        </>
      }
    >
      <p className="copilot-note">
        Copilot uses the current replay slice only: ticker scope, flow filters,
        visible alerts, classifier hits, packets, and option prints.
      </p>
      {disabledCopy ? <p className="copilot-note">{disabledCopy}</p> : null}
      {taskError ? <p className="copilot-error">{taskError}</p> : null}
      <TaskOutput
        taskId={activeTaskId}
        emptyMessage="Generate a replay postmortem to capture the cleanest read from the current session slice."
        onCancel={(taskId) => void handleCancel(taskId)}
      />
    </CopilotPane>
  );
}

export function ScreenCompilerPanel({
  currentFilters,
  onApplyFilters,
}: {
  currentFilters: OptionFlowFilters;
  onApplyFilters: (next: OptionFlowFilters) => void;
}) {
  const { bridgeAvailable, shellAvailable, state, runTask, cancelTask } = useDesktopAi();
  const [prompt, setPrompt] = useState("");
  const [activeTaskId, setActiveTaskId] = useState<string | null>(null);
  const [busy, setBusy] = useState(false);
  const [taskError, setTaskError] = useState<string | null>(null);
  const activeTask = useMemo(
    () => findTask(state.tasks, activeTaskId),
    [state.tasks, activeTaskId],
  );
  const disabledCopy = requireDesktopActionCopy(
    shellAvailable,
    bridgeAvailable,
    state.account.loggedIn,
  );
  const actionsDisabled = busy || !bridgeAvailable || !state.account.loggedIn;

  const handleCompile = async () => {
    const trimmedPrompt = prompt.trim();
    if (!trimmedPrompt) {
      setTaskError("Write a screen request first.");
      return;
    }

    setBusy(true);
    setTaskError(null);
    try {
      const result = await runTask({
        kind: "screen-compile",
        context: {
          prompt: trimmedPrompt,
          currentFilters,
        },
      });
      setActiveTaskId(result.taskId);
    } catch (error) {
      setTaskError(error instanceof Error ? error.message : String(error));
    } finally {
      setBusy(false);
    }
  };

  const handleCancel = async (taskId: string) => {
    setTaskError(null);
    try {
      await cancelTask(taskId);
    } catch (error) {
      setTaskError(error instanceof Error ? error.message : String(error));
    }
  };

  const compiledFilters = activeTask?.compiledScreen?.compiledFilters ?? null;
  const canRegenerate = shouldShowCopilotRegenerate(activeTask);

  return (
    <CopilotPane
      title="Natural-language screens"
      eyebrow="Tape workflow"
      actions={
        <>
          <Link className="terminal-button" href="/settings">
            AI settings
          </Link>
          <button
            className="terminal-button terminal-button-primary"
            type="button"
            onClick={() => void handleCompile()}
            disabled={actionsDisabled}
          >
            {busy ? "Compiling" : canRegenerate ? "Regenerate" : "Compile screen"}
          </button>
        </>
      }
    >
      <div className="copilot-inline-form">
        <label className="copilot-field">
          <span className="copilot-field-label">Prompt</span>
          <textarea
            className="copilot-textarea"
            rows={4}
            value={prompt}
            onChange={(event) => setPrompt(event.target.value)}
            placeholder="High-notional single-name call buying near the ask, ignore ETFs, keep it signal-only."
          />
        </label>
        <div className="copilot-current-filters">
          <div className="copilot-list-title">Current filter baseline</div>
          <pre className="copilot-json-block">
            {JSON.stringify(currentFilters, null, 2)}
          </pre>
        </div>
      </div>
      {disabledCopy ? <p className="copilot-note">{disabledCopy}</p> : null}
      {taskError ? <p className="copilot-error">{taskError}</p> : null}
      {compiledFilters ? (
        <div className="copilot-apply-row">
          <button
            className="terminal-button"
            type="button"
            onClick={() => onApplyFilters(compiledFilters)}
          >
            Apply compiled filters
          </button>
        </div>
      ) : null}
      <TaskOutput
        taskId={activeTaskId}
        emptyMessage="Compile a natural-language screen to preview the translated filter set and rationale."
        onCancel={(taskId) => void handleCancel(taskId)}
      />
    </CopilotPane>
  );
}
