"use client";

import { useParams } from "next/navigation";
import { useEffect, useMemo, useRef, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { api, ServerRun, StepRun, WorkflowRun } from "@/lib/api";
import { Button } from "@/components/ui";

type CellKind = "done" | "fail" | "run" | "wait" | "skip" | "warn";

function cellKind(status: string): CellKind {
    switch (status) {
        case "success":
            return "done";
        case "failed":
            return "fail";
        case "running":
            return "run";
        case "skipped":
            return "skip";
        case "cancelled":
            return "warn";
        default:
            return "wait";
    }
}

const cellGlyph: Record<CellKind, string> = {
    done: "✓",
    fail: "✕",
    run: "●",
    wait: "○",
    skip: "–",
    warn: "!",
};

const cellClass: Record<CellKind, string> = {
    done: "bg-success/15 text-success",
    fail: "bg-danger/15 text-danger",
    run: "bg-accent/15 text-accent",
    wait: "text-border",
    skip: "text-text-secondary",
    warn: "bg-warning/15 text-warning",
};

type PillKind = "running" | "success" | "failed" | "neutral";

function pillKind(status: string): PillKind {
    if (status === "running") return "running";
    if (status === "success") return "success";
    if (status === "failed" || status === "cancelled") return "failed";
    return "neutral";
}

const pillClass: Record<PillKind, string> = {
    running: "text-accent border-accent/40 bg-accent/10",
    success: "text-success border-success/35 bg-success/10",
    failed: "text-danger border-danger/35 bg-danger/10",
    neutral: "text-text-secondary border-border bg-surface-2",
};

const pillLed: Record<PillKind, string> = {
    running: "bg-accent led-pulse",
    success: "bg-success",
    failed: "bg-danger",
    neutral: "bg-text-secondary",
};

function StatusPill({ status, small }: { status: string; small?: boolean }) {
    const kind = pillKind(status);
    return (
        <span className={`inline-flex items-center gap-2 rounded-full border font-mono font-semibold uppercase tracking-wide ${small ? "px-2 py-0.5 text-[10px]" : "px-2.5 py-1 text-xs"} ${pillClass[kind]}`}>
            <span className={`h-1.5 w-1.5 rounded-full ${pillLed[kind]}`} />
            {status}
        </span>
    );
}

function fmtDuration(ms: number): string {
    if (ms < 0) ms = 0;
    const s = Math.floor(ms / 1000);
    if (s < 60) return `${s}s`;
    const m = Math.floor(s / 60);
    const rem = s % 60;
    if (m < 60) return `${m}m ${rem}s`;
    const h = Math.floor(m / 60);
    return `${h}h ${m % 60}m`;
}

function stepDuration(st: StepRun, running: boolean, now: number): string {
    if (!st.started_at) return st.status === "queued" ? "queued" : "";
    const start = new Date(st.started_at).getTime();
    const end = st.finished_at ? new Date(st.finished_at).getTime() : running ? now : start;
    return fmtDuration(end - start);
}

function LogTerminal({ runId, server }: { runId: string; server: ServerRun }) {
    const [text, setText] = useState("");
    const preRef = useRef<HTMLDivElement>(null);
    const running = server.status === "running";
    const serverId = server.server_id;

    useEffect(() => {
        setText("");
        if (running) {
            const es = new EventSource(api.serverRunLogStreamUrl(runId, serverId), {
                withCredentials: true,
            });
            es.onmessage = (e) => setText((t) => t + e.data + "\n");
            es.addEventListener("done", () => es.close());
            es.onerror = () => es.close();
            return () => es.close();
        }
        api.getServerRunLog(runId, serverId)
            .then(setText)
            .catch(() => setText(""));
    }, [running, runId, serverId]);

    useEffect(() => {
        preRef.current?.scrollTo(0, preRef.current.scrollHeight);
    }, [text]);

    const activeStep = server.steps.find((s) => s.status === "running") ?? [...server.steps].reverse().find((s) => s.started_at);

    return (
        <div className="overflow-hidden rounded-xl border border-border bg-well">
            <div className="flex items-center justify-between gap-2 border-b border-border bg-surface px-4 py-3">
                <span className="truncate font-mono text-[13px] font-semibold text-text-primary">
                    {activeStep ? activeStep.name : "Output"} <span className="font-normal text-text-secondary">{server.hostname}</span>
                </span>
                {running && (
                    <span className="inline-flex items-center gap-1.5 font-mono text-[10.5px] uppercase tracking-wide text-accent">
                        <span className="h-1.5 w-1.5 rounded-full bg-accent led-pulse" />
                        Streaming
                    </span>
                )}
            </div>
            <div ref={preRef} className="max-h-[340px] overflow-auto whitespace-pre-wrap px-4 py-3.5 font-mono text-[12.5px] leading-relaxed text-text-secondary">
                {text ? <LogLines text={text} /> : running ? "Waiting for output…" : "No output."}
                {running && text && <span className="ml-0.5 inline-block h-3.5 w-[7px] translate-y-[2px] bg-accent caret-blink align-baseline" />}
            </div>
        </div>
    );
}

const TS_RE = /^\[(\d{4}-\d{2}-\d{2}T[\d:.]+Z)\]\s?(.*)$/;

function LogLines({ text }: { text: string }) {
    const lines = text.replace(/\n$/, "").split("\n");
    return (
        <>
            {lines.map((line, i) => {
                const m = TS_RE.exec(line);
                if (!m) {
                    return (
                        <span key={i} className="block">
                            {line || " "}
                        </span>
                    );
                }
                const local = new Date(m[1]).toLocaleTimeString([], { hour12: false });
                const body = m[2];
                const isMarker = body.startsWith("=====");
                return (
                    <span key={i} className="block">
                        <span className="select-none text-text-tertiary" title={m[1]}>
                            {local}{" "}
                        </span>
                        <span className={isMarker ? "font-semibold text-accent" : ""}>{body || " "}</span>
                    </span>
                );
            })}
        </>
    );
}

function StepList({ server, now }: { server: ServerRun; now: number }) {
    const running = server.status === "running";
    return (
        <div className="overflow-hidden rounded-xl border border-border bg-surface">
            <div className="flex items-center justify-between gap-2 border-b border-border px-4 py-3">
                <span className="font-mono text-[13px] font-semibold text-text-primary">
                    Steps <span className="font-normal text-text-secondary">{server.steps.length}</span>
                </span>
                <StatusPill status={server.status} small />
            </div>
            <div className="flex flex-col gap-0.5 p-1.5">
                {server.steps.map((st) => {
                    const kind = cellKind(st.status);
                    return (
                        <div key={st.order} className={`grid grid-cols-[20px_1fr_auto] items-center gap-2.5 rounded-lg px-3 py-2.5 text-[13px] hover:bg-surface-2 ${st.status === "running" ? "bg-accent/[0.06]" : ""}`}>
                            <span className="text-right font-mono text-[11px] text-text-secondary">{String(st.order + 1).padStart(2, "0")}</span>
                            <span className="flex items-center gap-2 font-medium text-text-primary">
                                <span className={`font-mono ${cellClass[kind].replace(/bg-\S+/, "")}`}>{cellGlyph[kind]}</span>
                                {st.name}
                            </span>
                            <span className="text-right font-mono text-[10.5px] text-text-secondary">
                                {st.status === "failed" && <span className="text-danger">exit {st.exit_code} · </span>}
                                {st.attempts > 1 ? `${st.attempts} tries` : "1 try"}
                                {stepDuration(st, running, now) ? ` · ${stepDuration(st, running, now)}` : ""}
                            </span>
                        </div>
                    );
                })}
                {server.steps.length === 0 && <p className="px-3 py-2 text-xs text-text-secondary">No steps yet.</p>}
            </div>
        </div>
    );
}

interface Column {
    order: number;
    name: string;
}

function buildColumns(run: WorkflowRun): Column[] {
    const byOrder = new Map<number, string>();
    for (const sr of run.server_runs) {
        for (const st of sr.steps) {
            if (!byOrder.has(st.order)) byOrder.set(st.order, st.name);
        }
    }
    return [...byOrder.entries()].map(([order, name]) => ({ order, name })).sort((a, b) => a.order - b.order);
}

function ExecutionMatrix({ run, columns, selected, onSelect }: { run: WorkflowRun; columns: Column[]; selected: string; onSelect: (serverId: string) => void }) {
    return (
        <div className="overflow-x-auto rounded-xl border border-border bg-surface">
            <table className="w-full border-collapse font-mono text-[12.5px]">
                <thead>
                    <tr>
                        <th className="border-b border-border px-4 py-3 text-left align-bottom text-xs font-semibold uppercase tracking-wider text-text-primary">Server</th>
                        {columns.map((c) => (
                            <th key={c.order} className="whitespace-nowrap border-b border-border px-3.5 py-3 align-bottom text-[11px] font-medium text-text-secondary">
                                <span className="block text-[10px] text-border">{String(c.order + 1).padStart(2, "0")}</span>
                                {c.name}
                            </th>
                        ))}
                    </tr>
                </thead>
                <tbody>
                    {run.server_runs.map((sr) => {
                        const byOrder = new Map(sr.steps.map((s) => [s.order, s]));
                        const isSel = sr.server_id === selected;
                        return (
                            <tr key={sr.server_id} onClick={() => onSelect(sr.server_id)} className={`cursor-pointer ${isSel ? "bg-accent/5" : "hover:bg-white/[0.02]"}`}>
                                <th className="min-w-[240px] border-b border-r border-border px-4 py-3 text-left font-medium text-text-primary">
                                    <div className="flex items-center gap-3">
                                        <span className="flex-1 whitespace-nowrap">{sr.hostname}</span>
                                        <StatusPill status={sr.status} small />
                                    </div>
                                </th>
                                {columns.map((c) => {
                                    const st = byOrder.get(c.order);
                                    const kind = st ? cellKind(st.status) : "wait";
                                    return (
                                        <td key={c.order} className="relative border-b border-r border-border last:border-r-0">
                                            <span className="flex h-[54px] items-center justify-center">
                                                <span className={`relative flex h-[26px] w-[26px] items-center justify-center rounded-md ${cellClass[kind]}`}>
                                                    {kind === "run" && <span className="absolute inset-0 rounded-md border border-accent/50 cell-ring" />}
                                                    {cellGlyph[kind]}
                                                </span>
                                            </span>
                                        </td>
                                    );
                                })}
                            </tr>
                        );
                    })}
                </tbody>
            </table>
        </div>
    );
}

function SectionLabel({ children }: { children: React.ReactNode }) {
    return (
        <div className="mb-3 mt-8 flex items-center gap-2.5 font-mono text-[11px] uppercase tracking-widest text-text-secondary">
            {children}
            <span className="h-px flex-1 bg-border" />
        </div>
    );
}

export default function RunDetail() {
    const { runId } = useParams<{ runId: string }>();
    const queryClient = useQueryClient();
    const [selected, setSelected] = useState<string | null>(null);
    const [now, setNow] = useState(() => Date.now());

    const { data: run, isLoading } = useQuery({
        queryKey: ["run", runId],
        queryFn: () => api.getRun(runId),
        refetchInterval: (query) => (query.state.data?.status === "running" ? 2000 : false),
    });

    const running = run?.status === "running";

    useEffect(() => {
        if (!running) return;
        const t = setInterval(() => setNow(Date.now()), 1000);
        return () => clearInterval(t);
    }, [running]);

    const columns = useMemo(() => (run ? buildColumns(run) : []), [run]);

    const selectedServer = useMemo(() => {
        if (!run || run.server_runs.length === 0) return null;
        if (selected) {
            const match = run.server_runs.find((s) => s.server_id === selected);
            if (match) return match;
        }
        return run.server_runs.find((s) => s.status === "running") ?? run.server_runs[0];
    }, [run, selected]);

    const cancel = async () => {
        await api.cancelRun(runId);
        queryClient.invalidateQueries({ queryKey: ["run", runId] });
    };

    if (isLoading || !run) {
        return <div className="p-4 text-text-secondary sm:p-6 lg:p-8">Loading…</div>;
    }

    const totalSteps = run.server_runs.reduce((n, s) => n + s.steps.length, 0);
    const doneSteps = run.server_runs.reduce((n, s) => n + s.steps.filter((st) => st.status === "success").length, 0);
    const succeeded = run.server_runs.filter((s) => s.status === "success").length;
    const failed = run.server_runs.filter((s) => s.status === "failed" || s.status === "cancelled").length;

    const startMs = run.started_at ? new Date(run.started_at).getTime() : now;
    const endMs = run.finished_at ? new Date(run.finished_at).getTime() : now;
    const elapsed = fmtDuration(endMs - startMs);
    const ago = fmtDuration(now - startMs);

    return (
        <div className="p-4 sm:p-6 lg:p-8">
            {/* identity bar */}
            <div className="flex flex-wrap items-start justify-between gap-6">
                <div>
                    <div className="mb-2 font-mono text-xs uppercase tracking-wide text-text-secondary">Workflows / {run.name} / Runs</div>
                    <h1 className="text-[28px] font-semibold tracking-tight text-text-primary">{run.name}</h1>
                    <div className="mt-2.5 flex flex-wrap items-center gap-x-4 gap-y-1 font-mono text-[12.5px] text-text-secondary">
                        <span>
                            run <b className="font-medium text-text-primary">{run.run_id.slice(0, 8)}</b>
                        </span>
                        <span className="h-[3px] w-[3px] rounded-full bg-border" />
                        <span>
                            triggered by <b className="font-medium text-text-primary">{run.triggered_by || "n/a"}</b>
                        </span>
                        <span className="h-[3px] w-[3px] rounded-full bg-border" />
                        <span>
                            started <b className="font-medium text-text-primary">{ago}</b> ago
                        </span>
                        <span className="h-[3px] w-[3px] rounded-full bg-border" />
                        <span>
                            elapsed <b className="font-medium text-text-primary">{elapsed}</b>
                        </span>
                    </div>
                </div>
                <div className="flex items-center gap-3.5">
                    <StatusPill status={run.status} />
                    {running && (
                        <Button variant="danger" onClick={cancel}>
                            Cancel run
                        </Button>
                    )}
                </div>
            </div>

            {/* summary strip */}
            <div className="mt-6 grid grid-cols-2 gap-px overflow-hidden rounded-xl border border-border bg-border sm:grid-cols-4">
                <div className="bg-surface px-[18px] py-4">
                    <div className="font-mono text-[10.5px] uppercase tracking-wider text-text-secondary">Servers</div>
                    <div className="mt-1 font-mono text-[22px] font-semibold tabular-nums text-text-primary">{run.server_runs.length}</div>
                </div>
                <div className="bg-surface px-[18px] py-4">
                    <div className="font-mono text-[10.5px] uppercase tracking-wider text-text-secondary">Succeeded</div>
                    <div className="mt-1 font-mono text-[22px] font-semibold tabular-nums text-success">
                        {succeeded}
                        <small className="text-sm font-medium text-text-secondary"> / {run.server_runs.length}</small>
                    </div>
                </div>
                <div className="bg-surface px-[18px] py-4">
                    <div className="font-mono text-[10.5px] uppercase tracking-wider text-text-secondary">Failed</div>
                    <div className={`mt-1 font-mono text-[22px] font-semibold tabular-nums ${failed > 0 ? "text-danger" : "text-text-primary"}`}>{failed}</div>
                </div>
                <div className="bg-surface px-[18px] py-4">
                    <div className="font-mono text-[10.5px] uppercase tracking-wider text-text-secondary">Steps done</div>
                    <div className="mt-1 font-mono text-[22px] font-semibold tabular-nums text-text-primary">
                        {doneSteps}
                        <small className="text-sm font-medium text-text-secondary"> / {totalSteps}</small>
                    </div>
                </div>
            </div>

            {run.server_runs.length === 0 ? (
                <p className="mt-8 text-text-secondary">No servers targeted by this run.</p>
            ) : (
                <>
                    <SectionLabel>Execution matrix</SectionLabel>
                    <ExecutionMatrix run={run} columns={columns} selected={selectedServer?.server_id ?? ""} onSelect={setSelected} />

                    {selectedServer && (
                        <>
                            <SectionLabel>{selectedServer.hostname}&nbsp;·&nbsp;steps &amp; live output</SectionLabel>
                            <div className="grid grid-cols-1 items-start gap-4 md:grid-cols-[320px_1fr]">
                                <StepList server={selectedServer} now={now} />
                                <LogTerminal runId={run.run_id} server={selectedServer} />
                            </div>
                        </>
                    )}
                </>
            )}
        </div>
    );
}
