import "../../styles/palacecode.css";
import { useState, useEffect, useMemo, useCallback, useRef } from "preact/hooks";
import type { LinearIssue, ThreadData } from "../../lib/types";
import type { Attachment } from "../../lib/auto-attach";
import { useThreads } from "../../lib/use-threads";
import { autoAttach } from "../../lib/auto-attach";
import { attachThread as apiAttach, updateThreadStatus, deleteThread as apiDelete, sendChatStream } from "../../lib/api";
import { statusOf, applyStale, fmtDur } from "../../lib/format";
import { SpeechRecognizer } from "@palace/sdk/speech";
import { useGamepad } from "../../lib/use-gamepad";
import { Plus, Gamepad2 } from "lucide-preact";
import ThreadCard from "./ThreadCard";
import { ThreadDetail, DraftDetail } from "./DetailPanel";

const WINDOWS = [
  { label: "1h",  ms: 1 * 60 * 60 * 1000 },
  { label: "6h",  ms: 6 * 60 * 60 * 1000 },
  { label: "12h", ms: 12 * 60 * 60 * 1000 },
  { label: "1d",  ms: 24 * 60 * 60 * 1000 },
  { label: "3d",  ms: 3 * 24 * 60 * 60 * 1000 },
  { label: "7d",  ms: 7 * 24 * 60 * 60 * 1000 },
];

export default function Dashboard() {
  const [windowIdx, setWindowIdx] = useState(3); // default: 1d
  const [showWindowPicker, setShowWindowPicker] = useState(false);
  const [showArchived, setShowArchived] = useState(false);
  const { threads, refresh } = useThreads(3000, WINDOWS[windowIdx].ms);
  const [linearIssues, setLinearIssues] = useState<LinearIssue[]>([]);

  useEffect(() => {
    fetch("/code/api/linear-issues")
      .then((r) => r.ok ? r.json() : [])
      .then((data) => Array.isArray(data) && setLinearIssues(data))
      .catch(() => {});
  }, []);
  const [attachments, setAttachments] = useState<Record<string, Attachment>>({});

  useEffect(() => {
    fetch("/code/api/attach-thread")
      .then((r) => r.ok ? r.json() : [])
      .then((data: { threadId: string; linearIssue: string }[]) => {
        if (!Array.isArray(data)) return;
        const map: Record<string, Attachment> = {};
        for (const a of data) if (a.threadId && a.linearIssue) map[a.threadId] = { linearIssue: a.linearIssue };
        setAttachments(map);
      })
      .catch(() => {});
  }, []);

  const [selectedKey, setSelectedKey] = useState<string | null>(null);
  const [showDraft, setShowDraft] = useState(false);
  const [gamepadConnected, setGamepadConnected] = useState(false);
  const [splitPos, setSplitPos] = useState<number | null>(null);
  const splitRef = useRef<HTMLDivElement>(null);
  const dragStart = useRef({ pos: 0, size: 0 });
  const recognizer = useRef(new SpeechRecognizer());
  const pickerRef = useRef<HTMLDivElement>(null);

  const onDividerPointerDown = useCallback((e: PointerEvent) => {
    e.preventDefault();
    const mobile = window.innerWidth <= 768;
    const left = splitRef.current?.querySelector(".split-left") as HTMLElement | null;
    dragStart.current = {
      pos: mobile ? e.clientY : e.clientX,
      size: mobile ? (left?.offsetHeight ?? 200) : (left?.offsetWidth ?? 340),
    };
    (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
  }, []);

  const onDividerPointerMove = useCallback((e: PointerEvent) => {
    if (!(e.currentTarget as HTMLElement).hasPointerCapture(e.pointerId)) return;
    const mobile = window.innerWidth <= 768;
    const delta = (mobile ? e.clientY : e.clientX) - dragStart.current.pos;
    const raw = dragStart.current.size + delta;
    const container = splitRef.current;
    if (!container) return;
    const total = mobile ? container.offsetHeight : container.offsetWidth;
    setSplitPos(Math.max(120, Math.min(total - 120, raw)));
  }, []);


  // Close picker on outside click
  useEffect(() => {
    if (!showWindowPicker) return;
    const handler = (e: MouseEvent) => {
      if (pickerRef.current && !pickerRef.current.contains(e.target as Node)) setShowWindowPicker(false);
    };
    document.addEventListener("click", handler, true);
    return () => document.removeEventListener("click", handler, true);
  }, [showWindowPicker]);

  const enriched = useMemo(() => {
    const copy = threads.map((t) => {
      const c = { ...t };
      applyStale(c);
      return c;
    });
    return autoAttach(copy, linearIssues, attachments);
  }, [threads, linearIssues, attachments]);

  const { readyThreads, workingThreads, queuedThreads, waitingThreads, archivedThreads, doneThreads } = useMemo(() => {
    const sort = (arr: typeof enriched) => [...arr].sort((a, b) => (b.endedAt || b.startedAt) - (a.endedAt || a.startedAt));
    const working = enriched.filter((t) => t.status === "processing" || t.status === "stalled").sort((a, b) => b.startedAt - a.startedAt);
    const queued = enriched.filter((t) => t.status === "queued").sort((a, b) => a.startedAt - b.startedAt);
    const ready = sort(enriched.filter((t) => t.status === "replied"));
    const waiting = sort(enriched.filter((t) => t.status === "skipped"));
    const archived = sort(enriched.filter((t) => t.status === "archived"));
    const done = sort(enriched.filter((t) => !["processing", "stalled", "queued", "replied", "skipped", "archived"].includes(t.status)));
    return { readyThreads: ready, workingThreads: working, queuedThreads: queued, waitingThreads: waiting, archivedThreads: archived, doneThreads: done };
  }, [enriched]);

  // Auto-select first thread if nothing selected
  useEffect(() => {
    if (selectedKey || showDraft) return;
    const all = [...readyThreads, ...workingThreads, ...queuedThreads, ...waitingThreads, ...doneThreads];
    if (all.length > 0) {
      setSelectedKey(`${all[0].channel}:${all[0].threadId}`);
    }
  }, [readyThreads, workingThreads, queuedThreads, waitingThreads, doneThreads, selectedKey, showDraft]);

  const selectedThread = useMemo(() => {
    if (!selectedKey) return null;
    return enriched.find((t) => `${t.channel}:${t.threadId}` === selectedKey) || null;
  }, [enriched, selectedKey]);

  const handleAttach = useCallback(async (threadId: string, issueId: string) => {
    await apiAttach(threadId, issueId);
    setAttachments((prev) => ({ ...prev, [threadId]: { linearIssue: issueId } }));
    refresh();
  }, [refresh]);

  const handleChangeStatus = useCallback(async (channel: string, threadId: string, newStatus: string) => {
    await updateThreadStatus(channel, threadId, newStatus);
    refresh();
  }, [refresh]);

  const handleDelete = useCallback(async (channel: string, threadId: string) => {
    await apiDelete(channel, threadId);
    if (selectedKey === `${channel}:${threadId}`) setSelectedKey(null);
    refresh();
  }, [refresh, selectedKey]);

  const handleArchive = useCallback(async (channel: string, threadId: string) => {
    await updateThreadStatus(channel, threadId, "archived");
    refresh();
  }, [refresh]);

  const handleSelect = useCallback((key: string) => {
    setSelectedKey(key);
    setShowDraft(false);
  }, []);

  const handleNewChat = useCallback(() => {
    setShowDraft(true);
    setSelectedKey(null);
  }, []);

  const handleDraftSent = useCallback((threadId: string) => {
    setSelectedKey(`code:${threadId}`);
    refresh();
  }, [refresh]);

  const handleDebugThread = useCallback(async (thread: ThreadData) => {
    const sessionPath = thread.sessionFile
      ? `palaceplatform/channels/logs/sessions/${thread.sessionFile}`
      : null;
    const lines = [
      `Debug investigation requested for a stalled thread.`,
      ``,
      `Subject: ${thread.subject}`,
      `Channel: ${thread.channel}`,
      `Thread ID: ${thread.threadId}`,
      `Started: ${new Date(thread.startedAt).toISOString()}`,
      `Duration before stalling: ${fmtDur((thread.endedAt || Date.now()) - thread.startedAt)}`,
      thread.linearIssue ? `Linear issue: ${thread.linearIssue}${thread.linearTitle ? ` — ${thread.linearTitle}` : ""}` : null,
      sessionPath ? `Session log: ${sessionPath}` : null,
      ``,
      `Please read the session log, identify the last tool call and what it was doing, determine why the process stopped without completing, and report your findings.`,
    ].filter(Boolean).join("\n");
    const result = await sendChatStream(null, lines, () => {}, { title: `Debug: ${thread.subject}` });
    handleDraftSent(result.threadId);
  }, [handleDraftSent]);

  const handleDraftClose = useCallback(() => {
    setShowDraft(false);
  }, []);

  useGamepad({
    onConnected: setGamepadConnected,
    onMic() { (document.querySelector(".chat-reply-mic") as HTMLElement | null)?.click(); },
  });

  const activeCount = enriched.filter((t) => t.status === "processing").length;
  const nonArchived = enriched.filter((t) => t.status !== "archived");

  const renderGroup = (label: string, items: typeof readyThreads) => {
    if (items.length === 0) return null;
    return (
      <div class="thread-group">
        <div class="thread-group-label">{label}</div>
        {items.map((t) => {
          const key = `${t.channel}:${t.threadId}`;
          return (
            <ThreadCard
              key={key}
              thread={t}
              selected={key === selectedKey}
              onClick={() => handleSelect(key)}
              onChangeStatus={(s) => handleChangeStatus(t.channel, t.threadId, s)}
              onDelete={() => handleDelete(t.channel, t.threadId)}
              onArchive={() => handleArchive(t.channel, t.threadId)}
              onDebug={() => handleDebugThread(t)}
            />
          );
        })}
      </div>
    );
  };

  return (
    <>
      <div class="sec-hdr">
        <h2>Palace Code</h2>
        <div class="sec-hdr-right">
          <div class="window-picker-wrap" ref={pickerRef}>
            <button class="window-pill" onClick={(e) => { e.stopPropagation(); setShowWindowPicker(!showWindowPicker); }}>
              {showArchived ? "Archived" : `last ${WINDOWS[windowIdx].label}`}
            </button>
            {showWindowPicker && (
              <div class="window-picker-dd">
                {WINDOWS.map((w, i) => (
                  <div
                    key={w.label}
                    class={`window-picker-opt${i === windowIdx ? " active" : ""}`}
                    onClick={() => { setWindowIdx(i); setShowWindowPicker(false); }}
                  >
                    {w.label}
                  </div>
                ))}
                <div class="window-picker-divider" />
                <div
                  class={`window-picker-opt${showArchived ? " active" : ""}`}
                  onClick={() => { setShowArchived(!showArchived); setShowWindowPicker(false); }}
                >
                  {showArchived ? "✓ " : ""}Archived
                </div>
              </div>
            )}
          </div>
          {gamepadConnected && (
            <span class="gamepad-badge" title="Controller connected · ↕ navigate · Y new · B back">
              <Gamepad2 size={14} /> Controller
            </span>
          )}
          <button class="plus-btn" onClick={handleNewChat} title="New chat"><Plus size={18} /></button>
        </div>
      </div>
      <div class="split-view" ref={splitRef} style={splitPos !== null ? { "--split-pos": `${splitPos}px` } as any : undefined}>
        <div class="split-left">
          {nonArchived.length === 0 && !showArchived ? (
            <div class="empty">No threads</div>
          ) : (
            <>
              {renderGroup("Junwon to reply", readyThreads)}
              {renderGroup("Ace is working on this", workingThreads)}
              {renderGroup("Queued", queuedThreads)}
              {renderGroup("Waiting", waitingThreads)}
              {renderGroup("Done", doneThreads)}
              {showArchived && renderGroup("Archived", archivedThreads)}
            </>
          )}
        </div>
        <div
          class="split-divider"
          onPointerDown={onDividerPointerDown}
          onPointerMove={onDividerPointerMove}
        />
        <div class="split-right">
          {showDraft ? (
            <DraftDetail
              recognizer={recognizer.current}
              linearIssues={linearIssues}
              onSent={handleDraftSent}
              onClose={handleDraftClose}
            />
          ) : selectedThread ? (
            <ThreadDetail
              key={selectedKey}
              thread={selectedThread}
              linearIssues={linearIssues}
              onAttach={handleAttach}
              onChangeStatus={(s) => handleChangeStatus(selectedThread.channel, selectedThread.threadId, s)}
              onDelete={() => handleDelete(selectedThread.channel, selectedThread.threadId)}
              recognizer={recognizer.current}
              onChatSent={refresh}
            />
          ) : (
            <div class="detail-panel">
              <div class="detail-empty">Select a thread to view details</div>
            </div>
          )}
        </div>
      </div>
    </>
  );
}
