"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { useParams } from "next/navigation";
import useSWR from "swr";
import AppShell from "@/components/AppShell";
import TaskLiveStatus from "@/components/TaskLiveStatus";
import { ConfirmDialog, Modal, PageHeader, StatusBadge } from "@/components/ui";
import { useToast } from "@/components/toast";
import { api, fetcher, wsUrl } from "@/lib/api";
import { useAuth, roleAtLeast } from "@/lib/auth";
import { TaskEvent } from "@/lib/task-types";
import {
  LlmUsage,
  RunHistory,
  TaskArtifactsPanel,
  TaskCostPanel,
  TaskReportPanel,
  TaskRunHistoryPanel,
  TaskScopeCard,
  TaskTimelinePanel,
} from "@/components/task-detail-panels";

const MAX_EVENTS = 600;
const ACTIVE_STATUSES = ["running", "queued", "waiting_approval"];

type Event = TaskEvent;
type Approval = {
  id: number;
  tool_name: string;
  tool_args: any;
  risk: string | null;
  status: string;
};
type Task = {
  id: number;
  title: string;
  issue: string;
  status: string;
  report: any;
  error: string | null;
  memory_id: string | null;
  obsidian_path: string | null;
  vessel_id: number | null;
  vessel_name?: string | null;
  events?: Event[];
};

export default function TaskDetailPage() {
  const params = useParams();
  const id = params.id as string;
  const { user } = useAuth();
  const canAct = roleAtLeast(user?.role, "support");
  const toast = useToast();

  // Poll only while the task is active; stop once it settles (WS drives live updates).
  const taskActive = (data?: Task) => !!data && ACTIVE_STATUSES.includes(data.status);
  const { data: task, error: taskError, isLoading: taskLoading, mutate } = useSWR<Task>(
    `/api/tasks/${id}`,
    fetcher,
    { refreshInterval: (d) => (taskActive(d) ? 8000 : 0) }
  );
  const { data: approvals, mutate: mutateApprovals } = useSWR<Approval[]>(
    `/api/tasks/${id}/approvals`,
    fetcher,
    { refreshInterval: (d) => ((d || []).some((a) => a.status === "pending") ? 8000 : 0) }
  );

  const [events, setEvents] = useState<Event[]>([]);
  const [wsConnected, setWsConnected] = useState(false);
  const [continueOpen, setContinueOpen] = useState(false);
  const [retryOpen, setRetryOpen] = useState(false);
  const [cancelOpen, setCancelOpen] = useState(false);
  const [followUp, setFollowUp] = useState("");
  const [actionError, setActionError] = useState<string | null>(null);
  const [acting, setActing] = useState(false);
  const [decidingId, setDecidingId] = useState<number | null>(null);
  const [pinnedToBottom, setPinnedToBottom] = useState(true);
  const endRef = useRef<HTMLDivElement>(null);
  const scrollBoxRef = useRef<HTMLDivElement>(null);

  const appendEvent = useCallback((ev: Event) => {
    setEvents((prev) => {
      const key = `${ev.id || ""}-${ev.ts || ev.created_at || ""}-${ev.kind}-${ev.message || ""}`;
      if (prev.some((item) => `${item.id || ""}-${item.ts || item.created_at || ""}-${item.kind}-${item.message || ""}` === key)) {
        return prev;
      }
      const next = [...prev, ev];
      return next.length > MAX_EVENTS ? next.slice(next.length - MAX_EVENTS) : next;
    });
  }, []);

  useEffect(() => {
    if (!task?.events?.length) return;
    setEvents((prev) => {
      if (prev.length > 0) return prev;
      return task.events!.slice(-MAX_EVENTS);
    });
  }, [task?.events]);

  // Hydrate historical events once (so joining mid/post-run shows context),
  // then open a WebSocket with exponential-backoff reconnect.
  useEffect(() => {
    let ws: WebSocket | null = null;
    let closed = false;
    let retry = 0;
    let timer: ReturnType<typeof setTimeout>;

    setEvents([]);

    const connect = () => {
      ws = new WebSocket(wsUrl(`/api/tasks/${id}/stream`));
      ws.onopen = () => {
        retry = 0;
        setWsConnected(true);
      };
      ws.onmessage = (msg) => {
        try {
          const ev = JSON.parse(msg.data) as Event;
          appendEvent(ev);
          if (ev.kind === "approval") {
            mutateApprovals();
            mutate();
          }
          if (ev.kind === "report" || ev.kind === "error" || ev.kind === "cancelled") {
            mutate();
          }
        } catch {
          // ignore malformed frames
        }
      };
      ws.onclose = () => {
        setWsConnected(false);
        if (closed) return;
        retry += 1;
        const delay = Math.min(1000 * 2 ** retry, 15000);
        timer = setTimeout(connect, delay);
      };
      ws.onerror = () => ws?.close();
    };

    connect();
    return () => {
      closed = true;
      clearTimeout(timer);
      ws?.close();
    };
  }, [id, appendEvent, mutate, mutateApprovals]);

  useEffect(() => {
    if (pinnedToBottom) endRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [events, pinnedToBottom]);

  const onScroll = () => {
    const el = scrollBoxRef.current;
    if (!el) return;
    const nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
    setPinnedToBottom(nearBottom);
  };

  const decide = async (approvalId: number, approve: boolean) => {
    setDecidingId(approvalId);
    try {
      await api(`/api/tasks/${id}/approvals/${approvalId}`, {
        method: "POST",
        body: JSON.stringify({ approve }),
      });
      toast.success(approve ? "Approved" : "Rejected");
      mutateApprovals();
      mutate();
    } catch (err: any) {
      toast.error(err.message || "Action failed");
    } finally {
      setDecidingId(null);
    }
  };

  const continueTask = async (e: React.FormEvent) => {
    e.preventDefault();
    setActionError(null);
    setActing(true);
    try {
      await api(`/api/tasks/${id}/continue`, {
        method: "POST",
        body: JSON.stringify({ follow_up: followUp }),
      });
      setContinueOpen(false);
      setFollowUp("");
      setEvents([]);
      toast.success("Follow-up run queued");
      mutate();
    } catch (err: any) {
      setActionError(err.message);
      toast.error(err.message || "Could not continue task");
    } finally {
      setActing(false);
    }
  };

  const retryTask = async () => {
    setActing(true);
    try {
      await api(`/api/tasks/${id}/retry`, { method: "POST" });
      setEvents([]);
      setRetryOpen(false);
      toast.success("Task restarted");
      mutate();
    } catch (err: any) {
      setActionError(err.message);
      toast.error(err.message || "Could not restart task");
    } finally {
      setActing(false);
    }
  };

  const cancelTask = async () => {
    setActing(true);
    try {
      await api(`/api/tasks/${id}/cancel`, { method: "POST" });
      setCancelOpen(false);
      toast.success("Task stopped");
      mutate();
    } catch (err: any) {
      setActionError(err.message);
      toast.error(err.message || "Could not stop task");
    } finally {
      setActing(false);
    }
  };

  const report = task?.report;
  const llmUsage: LlmUsage[] = report?.llm_usage || [];
  const runs: RunHistory[] = report?.runs || [];
  const pendingApprovals = (approvals || []).filter((a) => a.status === "pending");
  const canContinue =
    canAct &&
    task &&
    !["running", "queued"].includes(task.status);
  const isRunning = task?.status === "running";
  const canStop = canAct && task && ["running", "queued"].includes(task.status);

  const escalateLlm = async (tier: "economy" | "premium") => {
    setActionError(null);
    setActing(true);
    try {
      await api(`/api/tasks/${id}/escalate-llm`, {
        method: "POST",
        body: JSON.stringify({ tier }),
      });
      mutate();
    } catch (err: any) {
      setActionError(err.message);
    } finally {
      setActing(false);
    }
  };

  return (
    <AppShell>
      <PageHeader
        backHref="/tasks"
        backLabel="Tasks"
        title={task?.title || `Task #${id}`}
        subtitle={
          task
            ? [
                task.vessel_name && `Vessel: ${task.vessel_name}`,
                task.report?.vessel_public_ip && `(${task.report.vessel_public_ip})`,
                task.issue,
              ]
                .filter(Boolean)
                .join(" · ")
            : undefined
        }
        action={
          task && (
            <div className="flex flex-wrap items-center gap-2">
              <StatusBadge status={task.status} />
              {report?.run_number > 1 && (
                <span className="badge bg-panel2 text-xs text-muted">Run {report.run_number}</span>
              )}
              {isRunning && canAct && (
                <>
                  <button
                    className="btn-ghost btn-sm border border-warn/40 text-warn"
                    onClick={() => escalateLlm("economy")}
                    disabled={acting}
                    title="Skip local LLM for remaining reasoning; use DeepSeek economy API"
                  >
                    Use Economy API
                  </button>
                  <button
                    className="btn-ghost btn-sm border border-accent2/40 text-accent2"
                    onClick={() => escalateLlm("premium")}
                    disabled={acting}
                    title="Skip local LLM for remaining reasoning; use premium cloud API"
                  >
                    Use Premium API
                  </button>
                </>
              )}
              {canStop && (
                <button
                  className="btn-danger btn-sm"
                  onClick={() => setCancelOpen(true)}
                  disabled={acting}
                  title="Stop this task if it is stuck or no longer needed"
                >
                  Stop task
                </button>
              )}
              {canContinue && (
                <>
                  <button className="btn-primary btn-sm" onClick={() => setContinueOpen(true)} disabled={acting}>
                    Continue investigation
                  </button>
                  <button className="btn-ghost btn-sm" onClick={() => setRetryOpen(true)} disabled={acting}>
                    Restart from scratch
                  </button>
                </>
              )}
            </div>
          )
        }
      />

      {actionError && <div className="mb-4 text-sm text-bad">{actionError}</div>}
      {taskError && !task && (
        <div className="mb-4 text-sm text-bad">Couldn’t load this task. Retrying…</div>
      )}

      {task && <TaskLiveStatus events={events} taskStatus={task.status} />}

      <div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
        <TaskTimelinePanel
          events={events}
          wsConnected={wsConnected}
          pinnedToBottom={pinnedToBottom}
          scrollBoxRef={scrollBoxRef}
          endRef={endRef}
          onScroll={onScroll}
          loading={taskLoading}
          onJump={() => {
            setPinnedToBottom(true);
            endRef.current?.scrollIntoView({ behavior: "smooth" });
          }}
        />

        <div className="space-y-6">
          {pendingApprovals.length > 0 && (
            <div className="card border-warn/40">
              <h2 className="mb-1 font-semibold text-warn">Approvals required</h2>
              <p className="mb-3 text-xs text-muted">
                Config-changing actions proposed by the agent. Approve before they can be applied.
              </p>
              <div className="space-y-3">
                {pendingApprovals.map((a) => (
                    <div key={a.id} className="rounded-lg bg-panel2 p-3">
                      <div className="font-mono text-sm text-accent2">{a.tool_name}</div>
                      {a.risk && <div className="mt-1 text-sm text-muted">{a.risk}</div>}
                      {canAct && (
                        <div className="mt-3 flex gap-2">
                          <button
                            className="btn-primary btn-sm"
                            onClick={() => decide(a.id, true)}
                            disabled={decidingId === a.id}
                          >
                            {decidingId === a.id ? "Working…" : "Approve"}
                          </button>
                          <button
                            className="btn-danger btn-sm"
                            onClick={() => decide(a.id, false)}
                            disabled={decidingId === a.id}
                          >
                            Reject
                          </button>
                        </div>
                      )}
                    </div>
                  ))}
              </div>
            </div>
          )}

          {report && (
            <>
              <TaskScopeCard task={task} report={report} />
              <TaskReportPanel report={report} pendingApprovals={pendingApprovals.length} />
              <TaskArtifactsPanel task={task} report={report} />
              <TaskCostPanel report={report} llmUsage={llmUsage} />
              <TaskRunHistoryPanel runs={runs} />
            </>
          )}

          {task?.error && (
            <div className="card border-bad/40">
              <h2 className="mb-2 font-semibold text-bad">Error</h2>
              <p className="text-sm">{task.error}</p>
            </div>
          )}
        </div>
      </div>

      <Modal open={continueOpen} onClose={() => setContinueOpen(false)} title="Continue investigation">
        <form onSubmit={continueTask} className="space-y-4">
          <p className="text-sm text-muted">
            The agent will use prior findings and run additional checks. Describe what to investigate next
            or why the issue is still unresolved.
          </p>
          <div>
            <label htmlFor="follow-up" className="label">
              Follow-up instructions
            </label>
            <textarea
              id="follow-up"
              className="input min-h-[120px]"
              value={followUp}
              onChange={(e) => setFollowUp(e.target.value)}
              placeholder="e.g. check asterisk SIP registrations and pfsense NAT rules for port 5060"
              required
            />
          </div>
          {actionError && <div className="text-sm text-bad">{actionError}</div>}
          <button className="btn-primary w-full" disabled={acting}>
            {acting ? "Queuing…" : "Run follow-up"}
          </button>
        </form>
      </Modal>

      <ConfirmDialog
        open={cancelOpen}
        title="Stop this task?"
        message="The agent will stop at the next checkpoint. You can restart or continue the investigation later."
        confirmLabel="Stop task"
        destructive
        busy={acting}
        onConfirm={cancelTask}
        onCancel={() => setCancelOpen(false)}
      />

      <ConfirmDialog
        open={retryOpen}
        title="Restart from scratch?"
        message="Run history and cost totals will be cleared, and the task will run again from the beginning."
        confirmLabel="Restart"
        destructive
        busy={acting}
        onConfirm={retryTask}
        onCancel={() => setRetryOpen(false)}
      />
    </AppShell>
  );
}
