import { useState, useEffect, useCallback } from "react";
import { useParams, useNavigate, Link } from "react-router-dom";
import {
  experiments,
  runs as runsApi,
  ApiError,
} from "../api/client";
import type {
  ExperimentResponse,
  RunResponse,
} from "../api/client";
import { useExperimentWS } from "../hooks/useExperimentWS";
import type { WsEvent, WsEventType, ConnectionStatus } from "../hooks/useExperimentWS";
import Leaderboard from "../components/Leaderboard";
import type { LeaderboardRow } from "../components/Leaderboard";
import Timeline from "../components/Timeline";
import type { TimelineEntry } from "../components/Timeline";
import SteeringControls from "../components/SteeringControls";

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

let _timelineIdCounter = 0;
function nextTimelineId(): string {
  _timelineIdCounter += 1;
  return `tl-${_timelineIdCounter}`;
}

function configSummary(config?: Record<string, unknown>): string {
  if (!config) return "—";
  const model = config.model ?? config.model_used ?? "";
  const temp = config.temperature != null ? `t=${config.temperature}` : "";
  const parts = [model, temp].filter(Boolean);
  return parts.length > 0 ? parts.join(" ") : JSON.stringify(config).slice(0, 60);
}


// ---------------------------------------------------------------------------
// Connection Status Indicator
// ---------------------------------------------------------------------------

function ConnectionIndicator({ status }: { status: ConnectionStatus }) {
  const colors: Record<ConnectionStatus, string> = {
    connected: "bg-green-500",
    connecting: "bg-amber-500 animate-pulse",
    disconnected: "bg-red-500",
  };

  return (
    <span className="inline-flex items-center gap-1.5 text-xs text-slate-500 dark:text-slate-400">
      <span
        data-testid="connection-indicator"
        className={`h-2 w-2 rounded-full ${colors[status]}`}
      />
      {status === "connected"
        ? "Live"
        : status === "connecting"
          ? "Connecting…"
          : "Disconnected"}
    </span>
  );
}

// ---------------------------------------------------------------------------
// Live Page
// ---------------------------------------------------------------------------

export default function LivePage() {
  const { id } = useParams<{ id: string }>();
  const navigate = useNavigate();

  // Experiment state
  const [experiment, setExperiment] = useState<ExperimentResponse | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [expStatus, setExpStatus] = useState("idle");

  // Timeline
  const [timeline, setTimeline] = useState<TimelineEntry[]>([]);
  const [autoScroll, setAutoScroll] = useState(true);
  const [eventFilter, setEventFilter] = useState<WsEventType | "all">("all");
  // Leaderboard
  const [leaderboard, setLeaderboard] = useState<LeaderboardRow[]>([]);
  const [bestRunId, setBestRunId] = useState<string | null>(null);

  // Progress
  const [progress, setProgress] = useState({
    completed: 0,
    total: 0,
    cache_hits: 0,
    tokens_total: 0,
    cost_total: 0,
  });

  // -------------------------------------------------------------------------
  // Load experiment
  // -------------------------------------------------------------------------

  const loadExperiment = useCallback(async () => {
    if (!id) return;
    setLoading(true);
    setError(null);
    try {
      const exp = await experiments.get(id);
      setExperiment(exp);
      setExpStatus(exp.status);
    } catch (err: unknown) {
      if (err instanceof ApiError) {
        setError(`Failed to load experiment (${err.status}).`);
      } else {
        setError("Network error. Is the server running?");
      }
    } finally {
      setLoading(false);
    }
  }, [id]);

  // -------------------------------------------------------------------------
  // Load initial leaderboard
  // -------------------------------------------------------------------------

  const loadLeaderboard = useCallback(async () => {
    if (!id) return;
    try {
      const resp = await runsApi.leaderboard(id);
      const rows: LeaderboardRow[] = resp.items.map((r: RunResponse) => ({
        run_id: r.id,
        config_summary: configSummary(r.config),
        scores: {},
        weighted_total: 0,
        status: r.status,
        cached: false,
      }));
      setLeaderboard(rows);
      if (rows.length > 0) {
        setBestRunId(rows[0].run_id);
      }
    } catch {
      // Non-critical — leaderboard will populate via WebSocket
    }
  }, [id]);

  useEffect(() => {
    loadExperiment();
    loadLeaderboard();
  }, [loadExperiment, loadLeaderboard]);

  // -------------------------------------------------------------------------
  // Process incoming WS events
  // -------------------------------------------------------------------------

  const processEvent = useCallback(
    (evt: WsEvent) => {
      const now = new Date(evt.timestamp ?? Date.now());

      // Build timeline entry
      let message = "";
      let detail: string | undefined;

      switch (evt.type) {
        case "run.started":
          message = `Run started: ${configSummary(evt.config)}`;
          break;
        case "run.completed":
          message = `Run completed: ${configSummary(evt.config)}`;
          detail =
            evt.weighted_total != null
              ? `Score: ${evt.weighted_total.toFixed(3)}`
              : undefined;
          break;
        case "new_best_found":
          message = `New best config found!`;
          detail =
            evt.weighted_total != null
              ? `Score: ${evt.weighted_total.toFixed(3)}`
              : undefined;
          break;
        case "cache_hit":
          message = `Cache hit: ${configSummary(evt.config)}`;
          break;
        case "run.failed":
          message = `Run failed: ${evt.error ?? "unknown error"}`;
          break;
        case "sweep.progress":
          message = `Progress: ${evt.progress?.completed ?? 0}/${evt.progress?.total ?? 0} runs`;
          break;
        case "sweep.completed":
          message = "Sweep completed!";
          setExpStatus("completed");
          break;
        default:
          message = evt.type;
      }

      setTimeline((prev) => [
        ...prev,
        {
          id: nextTimelineId(),
          type: evt.type,
          run_id: evt.run_id,
          message,
          detail,
          timestamp: now,
        },
      ]);

      // Update leaderboard
      if (
        (evt.type === "run.completed" || evt.type === "new_best_found") &&
        evt.run_id
      ) {
        const row: LeaderboardRow = {
          run_id: evt.run_id,
          config_summary: configSummary(evt.config),
          scores: evt.scores ?? {},
          weighted_total: evt.weighted_total ?? 0,
          status: "completed",
          cached: evt.cached ?? false,
        };

        setLeaderboard((prev) => {
          const existing = prev.findIndex((r) => r.run_id === row.run_id);
          if (existing >= 0) {
            const next = [...prev];
            next[existing] = row;
            return next;
          }
          return [...prev, row];
        });

        if (evt.type === "new_best_found") {
          setBestRunId(evt.run_id);
        }
      }

      if (evt.type === "cache_hit" && evt.run_id) {
        const row: LeaderboardRow = {
          run_id: evt.run_id,
          config_summary: configSummary(evt.config),
          scores: evt.scores ?? {},
          weighted_total: evt.weighted_total ?? 0,
          status: "completed",
          cached: true,
        };
        setLeaderboard((prev) => {
          const existing = prev.findIndex((r) => r.run_id === row.run_id);
          if (existing >= 0) return prev;
          return [...prev, row];
        });
      }

      // Update progress
      if (evt.progress) {
        setProgress(evt.progress);
      }
    },
    [],
  );

  // -------------------------------------------------------------------------
  // WebSocket connection via hook
  // -------------------------------------------------------------------------

  const { connectionStatus } = useExperimentWS(id, {
    onEvent: processEvent,
    enabled: !loading && !error,
  });

  // -------------------------------------------------------------------------
  // Render
  // -------------------------------------------------------------------------

  if (loading) {
    return (
      <div className="min-h-screen bg-gradient-to-br from-indigo-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 flex items-center justify-center">
        <p className="text-slate-500 dark:text-slate-400 animate-pulse">
          Loading experiment…
        </p>
      </div>
    );
  }

  if (error) {
    return (
      <div className="min-h-screen bg-gradient-to-br from-indigo-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 px-4 py-8">
        <div className="mx-auto max-w-2xl">
          <div
            role="alert"
            className="rounded-xl bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 p-6 text-center"
          >
            <p className="text-red-700 dark:text-red-300">{error}</p>
            <button
              type="button"
              onClick={loadExperiment}
              className="mt-3 text-sm font-medium text-indigo-600 dark:text-indigo-400 hover:text-indigo-500"
            >
              Retry
            </button>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-gradient-to-br from-indigo-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 px-4 py-6">
      <div className="mx-auto max-w-7xl">
        {/* Header */}
        <div className="mb-6 flex flex-wrap items-center justify-between gap-4">
          <div>
            <div className="flex items-center gap-3">
              <Link
                to={`/experiments/${id}`}
                className="text-sm text-indigo-600 dark:text-indigo-400 hover:text-indigo-500"
              >
                &larr; Experiment
              </Link>
              <ConnectionIndicator status={connectionStatus} />
            </div>
            <h1 className="mt-1 text-2xl font-bold text-slate-900 dark:text-white">
              {experiment?.name ?? "Live Dashboard"}
            </h1>
            {experiment?.description && (
              <p className="mt-0.5 text-sm text-slate-500 dark:text-slate-400">
                {experiment.description}
              </p>
            )}
          </div>
        </div>

        {/* Main layout: 60/40 split */}
        <div className="grid grid-cols-1 lg:grid-cols-5 gap-6">
          {/* Left column — 60% */}
          <div className="lg:col-span-3 space-y-6">
            <Timeline
              entries={timeline}
              eventFilter={eventFilter}
              onEventFilterChange={(f) => setEventFilter(f as WsEventType | "all")}
              autoScroll={autoScroll}
              onAutoScrollToggle={() => setAutoScroll(!autoScroll)}
            />
          </div>

          {/* Right column — 40% */}
          <div className="lg:col-span-2 space-y-6">
            {/* Steering Controls */}
            <div className="rounded-xl bg-white dark:bg-slate-800 shadow ring-1 ring-slate-200 dark:ring-slate-700 p-4">
              <h2 className="text-lg font-semibold text-slate-900 dark:text-white mb-4">
                Controls
              </h2>
              <SteeringControls
                experimentId={id!}
                experimentStatus={expStatus}
                progress={progress}
                onStatusChange={setExpStatus}
              />
            </div>

            {/* Leaderboard */}
            <div className="rounded-xl bg-white dark:bg-slate-800 shadow ring-1 ring-slate-200 dark:ring-slate-700 overflow-hidden">
              <div className="border-b border-slate-200 dark:border-slate-700 px-4 py-3">
                <h2 className="text-lg font-semibold text-slate-900 dark:text-white">
                  Leaderboard
                </h2>
              </div>
              <div className="p-4">
                <Leaderboard rows={leaderboard} bestRunId={bestRunId} />
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
