"use client";

import { useEffect, useMemo, useRef, useState } from "react";

import { DataTable } from "@/components/data-table";
import { MetricCard } from "@/components/metric-card";
import { SectionHeading } from "@/components/section-heading";
import { SignalLineChart } from "@/components/line-chart";
import { StatusBadge } from "@/components/status-badge";
import {
  getDatasetRows,
  getMicrostructureFeatures,
  getMicrostructureHealth,
  getMicrostructureRegime,
  getMicrostructureSignalSeries,
  getMicrostructureSignals,
} from "@/lib/api/client";
import type {
  DatasetRowsResponse,
  MicrostructureFeatureResponse,
  MicrostructureHealth,
  MicrostructureRegimeResponse,
  MicrostructureSignalSeriesResponse,
  MicrostructureSignalSummary,
} from "@/lib/api/types";
import { formatMetricValue } from "@/lib/format";
import { DEFAULT_INITIAL_RANGE_DAYS, RANGE_OPTIONS, rangeStartIso, seriesMaxPoints } from "@/lib/ranges";
import { useStream } from "@/lib/ws/use-stream";

import { MICROSTRUCTURE_SECTIONS } from "./microstructure-config";

type WorkspaceState = {
  health: MicrostructureHealth | null;
  signals: MicrostructureSignalSummary[];
  signalSeries: MicrostructureSignalSeriesResponse | null;
  featureSeries: MicrostructureFeatureResponse | null;
  regimeSeries: MicrostructureRegimeResponse | null;
  calibrationRows: DatasetRowsResponse | null;
  backtestRows: DatasetRowsResponse | null;
};

export function MicrostructureWorkspace({
  section,
  initialAsset,
  initialHorizon,
}: {
  section: string;
  initialAsset?: string;
  initialHorizon?: string;
}) {
  const config = MICROSTRUCTURE_SECTIONS.find((item) => item.id === section) ?? MICROSTRUCTURE_SECTIONS[0];
  const [asset, setAsset] = useState(initialAsset ?? "BTC");
  const [horizon, setHorizon] = useState(initialHorizon ?? "1m");
  const [rangeDays, setRangeDays] = useState(DEFAULT_INITIAL_RANGE_DAYS);
  const [quality, setQuality] = useState("all");
  const [state, setState] = useState<WorkspaceState>({
    health: null,
    signals: [],
    signalSeries: null,
    featureSeries: null,
    regimeSeries: null,
    calibrationRows: null,
    backtestRows: null,
  });
  const [fetching, setFetching] = useState(false);
  const inFlightRef = useRef(false);
  const pendingRef = useRef(false);
  const abortRef = useRef<AbortController | null>(null);

  const start = useMemo(() => {
    return rangeStartIso(rangeDays);
  }, [rangeDays]);

  useStream((event) => {
    if (event.event === "signal.updated" || event.event === "engine.updated" || event.event === "dataset.updated") {
      void refresh();
    }
  });

  useEffect(() => {
    void refresh();
  }, [section, asset, horizon, start, quality]);

  async function refresh() {
    if (inFlightRef.current) {
      pendingRef.current = true;
      return;
    }
    abortRef.current?.abort();
    const controller = new AbortController();
    abortRef.current = controller;
    inFlightRef.current = true;
    setFetching(true);
    try {
      const signalId = `${asset.toLowerCase()}_microstructure_${horizon}`;
      const [health, signals, signalSeries, featureSeries, regimeSeries, calibrationRows, backtestRows] = await Promise.all([
        getMicrostructureHealth(controller.signal),
        getMicrostructureSignals(),
        getMicrostructureSignalSeries(
          signalId,
          start,
          undefined,
          quality === "all" ? undefined : quality,
          controller.signal,
          seriesMaxPoints(rangeDays),
        ),
        getMicrostructureFeatures(
          asset,
          horizon,
          start,
          undefined,
          quality === "all" ? undefined : quality,
          seriesMaxPoints(rangeDays),
          controller.signal,
        ),
        getMicrostructureRegime(
          asset,
          start,
          undefined,
          quality === "all" ? undefined : quality,
          seriesMaxPoints(rangeDays),
          controller.signal,
        ),
        getDatasetRows("microstructure_training_run", { symbol: asset, source: "tcn_boost_fusion", limit: 30 }),
        getDatasetRows("microstructure_backtest_run", { symbol: asset, source: "tcn_boost_fusion", limit: 30 }),
      ]);
      if (!controller.signal.aborted) {
        setState({ health, signals, signalSeries, featureSeries, regimeSeries, calibrationRows, backtestRows });
      }
    } finally {
      if (abortRef.current === controller) abortRef.current = null;
      inFlightRef.current = false;
      setFetching(false);
      if (pendingRef.current) {
        pendingRef.current = false;
        void refresh();
      }
    }
  }

  const chartData = buildChartData(config.id, state);
  const tableRows = buildTableRows(config.id, state);
  const latestValues = pickLatestValues(config.id, state);

  return (
    <div className="pageStack">
      <SectionHeading kicker="Microstructure" title={config.title} description={config.description} />
      <div className="twoCol">
        <div className="twoColNarrow">
          <div className="panel">
            <div className="toolbar" style={{ flexDirection: "column", alignItems: "stretch", gap: 8 }}>
              <label>
                Asset
                <select value={asset} onChange={(event) => setAsset(event.target.value)}>
                  <option value="BTC">BTC</option>
                  <option value="ETH">ETH</option>
                </select>
              </label>
              <label>
                Horizon
                <select value={horizon} onChange={(event) => setHorizon(event.target.value)}>
                  <option value="30s">30s</option>
                  <option value="1m">1m</option>
                  <option value="2m">2m</option>
                  <option value="5m">5m</option>
                </select>
              </label>
              <label>
                Range
                <select value={rangeDays} onChange={(event) => setRangeDays(Number(event.target.value))}>
                  {RANGE_OPTIONS.map((days) => (
                    <option key={days} value={days}>{days}d</option>
                  ))}
                </select>
              </label>
              <label>
                Quality
                <select value={quality} onChange={(event) => setQuality(event.target.value)}>
                  <option value="all">all</option>
                  <option value="ok">ok</option>
                  <option value="imported">imported</option>
                  <option value="insufficient_history">insufficient_history</option>
                </select>
              </label>
            </div>
          </div>
          <StatusBadge label={fetching ? "LOADING" : (state.health?.engine_status ?? "COLD").toUpperCase()} tone={state.health?.status === "ok" ? "ok" : "warn"} />
          <div style={{ display: "flex", flexDirection: "column", gap: 0 }}>
            {Object.entries(latestValues).slice(0, 4).map(([key, value]) => (
              <MetricCard
                key={key}
                label={key.replace(/_/g, " ")}
                value={typeof value === "number" ? formatMetricValue(value, key) : String(value ?? "—")}
                tone="neutral"
              />
            ))}
          </div>
        </div>
        <div className="twoColWide">
          <SignalLineChart data={chartData} lines={config.id === "predictions" ? [
            { key: "prob_spread", label: "P(Spread)", color: "#6ac2ff" },
            { key: "expected_move_bps", label: "Expected Move", color: "#ffd166", yAxisId: "right" },
          ] : config.lines} dualAxis loading={fetching} />
          <div className="panel">
            <div className="panelHeader"><h3>How to use this</h3></div>
            <p>{config.playbook.whatItMeasures}</p>
            <ul className="summaryList">
              {config.playbook.interpretation.map((item) => <li key={item}><span>{item}</span></li>)}
            </ul>
            <div className="panelHeader"><h3>Trade expressions</h3></div>
            <ul className="summaryList">
              {config.playbook.tradeExpressions.map((item) => <li key={item}><span>{item}</span></li>)}
            </ul>
            <div className="panelHeader"><h3>Caveats</h3></div>
            <ul className="summaryList">
              {config.playbook.caveats.map((item) => <li key={item}><span>{item}</span></li>)}
            </ul>
          </div>
          <DataTable columns={config.id === "predictions" ? ["ts", "direction", "signal_state", "paper_position", "prob_up", "prob_down", "tradability_score", "expected_move_bps", "quality_flag"] : config.tableColumns} rows={tableRows} statusColumn="quality_flag" loading={fetching} />
        </div>
      </div>
    </div>
  );
}

function pickLatestValues(section: string, state: WorkspaceState): Record<string, unknown> {
  if (section === "engine-health") return state.health ?? {};
  if (section === "regime") return state.regimeSeries?.latest_values ?? {};
  if (section === "features") return state.featureSeries?.latest_values ?? {};
  if (section === "calibration") return state.calibrationRows?.rows?.[0] ?? {};
  if (section === "backtests") return state.backtestRows?.rows?.[0] ?? {};
  return state.signalSeries?.latest_values ?? {};
}

function buildChartData(
  section: string,
  state: WorkspaceState,
): Record<string, string | number | null>[] {
  if (section === "regime") {
    return (state.regimeSeries?.points ?? []).map((point) => ({
      ts: point.ts,
      ...normalizeRecord(point.values),
    }));
  }
  if (section === "features") {
    return (state.featureSeries?.points ?? []).map((point) => ({
      ts: point.ts,
      ...normalizeRecord(point.values),
    }));
  }
  if (section === "calibration") {
    return (state.calibrationRows?.rows ?? []).map((row) => ({
      ts: String(row.ts ?? row.run_ts ?? ""),
      ...normalizeRecord(row),
    }));
  }
  if (section === "backtests") {
    return (state.backtestRows?.rows ?? []).map((row) => ({
      ts: String(row.ts ?? row.run_ts ?? ""),
      ...normalizeRecord(row),
    }));
  }
  if (section === "engine-health") {
    return state.health ? [{ ts: state.health.last_prediction_ts ?? new Date().toISOString(), signals_available: state.health.signals_available }] : [];
  }
  // Predictions: compute prob_spread
  return (state.signalSeries?.points ?? []).map((point) => ({
    ts: point.ts,
    prob_up: point.prob_up,
    prob_down: point.prob_down,
    prob_spread: point.prob_up != null && point.prob_down != null ? point.prob_up - point.prob_down : null,
    expected_move_bps: point.expected_move_bps,
    tradability_score: point.tradability_score,
    confidence: point.confidence,
    ...normalizeRecord(point.values),
  }));
}

function normalizeRecord(
  record: Record<string, unknown>,
): Record<string, string | number | null> {
  return Object.fromEntries(
    Object.entries(record).map(([key, value]) => {
      if (typeof value === "number" || typeof value === "string" || value === null) {
        return [key, value];
      }
      if (typeof value === "boolean") {
        return [key, value ? 1 : 0];
      }
      return [key, null];
    }),
  );
}

function buildTableRows(section: string, state: WorkspaceState): Record<string, unknown>[] {
  if (section === "regime") {
    return (state.regimeSeries?.points ?? []).slice(-20).reverse().map((point) => ({ ts: point.ts, regime_state: point.regime_state, quality_flag: point.quality_flag, ...point.values }));
  }
  if (section === "features") {
    return (state.featureSeries?.points ?? []).slice(-20).reverse().map((point) => ({ ts: point.ts, quality_flag: point.quality_flag, ...point.values }));
  }
  if (section === "calibration") {
    return state.calibrationRows?.rows ?? [];
  }
  if (section === "backtests") {
    return state.backtestRows?.rows ?? [];
  }
  if (section === "engine-health") {
    return state.health ? [state.health as unknown as Record<string, unknown>] : [];
  }
  // Predictions: 10 rows with direction column
  return (state.signalSeries?.points ?? []).slice(-10).reverse().map((point) => {
    const probUp = point.prob_up ?? 0;
    const probDown = point.prob_down ?? 0;
    let direction = "FLAT";
    if (probUp > probDown + 0.05) direction = "UP";
    else if (probDown > probUp + 0.05) direction = "DOWN";
    return {
      ts: point.ts,
      direction,
      signal_state: point.signal_state,
      paper_position: point.paper_position,
      prob_up: point.prob_up,
      prob_down: point.prob_down,
      tradability_score: point.tradability_score,
      expected_move_bps: point.expected_move_bps,
      quality_flag: point.quality_flag,
      ...point.values,
    };
  });
}
