import { useEffect, useRef, useState, useCallback } from "preact/hooks";
import type { ThreadData, LinearIssue, SessionEntry } from "../../lib/types";
import type { SpeechRecognizer } from "@palace/sdk/speech";
import { useSpeechRecognizer } from "@palace/sdk/speech/preact";
import { useSessionLog } from "../../lib/use-session-log";
import { fmtSessTs, typeClass, formatTypeLabel } from "../../lib/format";
import { sendChatStream, attachThread } from "../../lib/api";
import { renderMarkdown } from "../../lib/render-md";
import { X, Mic, SendHorizontal, ChevronRight } from "lucide-preact";


interface ThreadDetailProps {
  thread: ThreadData;
  linearIssues: LinearIssue[];
  onAttach: (threadId: string, issueId: string) => void;
  onChangeStatus: (newStatus: string) => void;
  onDelete?: () => void;
  recognizer: SpeechRecognizer;
  onChatSent: () => void;
}

interface DraftDetailProps {
  recognizer: SpeechRecognizer;
  linearIssues: LinearIssue[];
  onSent: (threadId: string) => void;
  onClose: () => void;
}

type ChatMsg = { role: "user" | "ace"; content: string };

export function ThreadDetail({ thread, linearIssues, onAttach, onChangeStatus, onDelete, recognizer, onChatSent }: ThreadDetailProps) {
  const isActive = thread.status === "processing";
  const { entries, loading } = useSessionLog(thread.sessionFile, true, isActive);
  const logRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (logRef.current) {
      logRef.current.scrollTop = logRef.current.scrollHeight;
    }
  }, [entries]);

  return (
    <div class="detail-panel">
      <div class="detail-log" ref={logRef}>
        {thread.sessionFile && loading && entries.length === 0 && (
          <div class="sess-loading">Loading session log...</div>
        )}
        {thread.sessionFile && !loading && entries.length === 0 && (
          <div class="sess-none">No session log entries yet.</div>
        )}
        {thread.sessionFile && entries.map((e, i) => (
          <LogEntry key={i} entry={e} />
        ))}
        {!thread.sessionFile && thread.logLines.map((l, i) => (
          <div key={i} class="sess-row"><span class="sess-content">{l}</span></div>
        ))}
      </div>
      {thread.channel === "code" && (
        <ChatInput threadId={thread.threadId} recognizer={recognizer} onSent={onChatSent} />
      )}
    </div>
  );
}

export function DraftDetail({ recognizer, linearIssues, onSent, onClose }: DraftDetailProps) {
  const inputRef = useRef<HTMLInputElement>(null);
  const titleRef = useRef<HTMLInputElement>(null);
  const logRef = useRef<HTMLDivElement>(null);
  const [busy, setBusy] = useState(false);
  const [history, setHistory] = useState<ChatMsg[]>([]);
  const [streamingText, setStreamingText] = useState<string | null>(null);
  const [status, setStatus] = useState<string | null>(null);
  const [attachedIssue, setAttachedIssue] = useState<string | null>(null);
  const [attachOpen, setAttachOpen] = useState(false);
  const [activeThreadId, setActiveThreadId] = useState<string | null>(null);

  const onTranscribed = useCallback((text: string) => {
    if (inputRef.current) {
      inputRef.current.value += (inputRef.current.value ? " " : "") + text;
      inputRef.current.focus();
    }
  }, []);

  const { recording, transcribing, toggle } = useSpeechRecognizer(recognizer, { onResult: onTranscribed, onStatus: setStatus });

  useEffect(() => {
    if (logRef.current) {
      logRef.current.scrollTop = logRef.current.scrollHeight;
    }
  }, [history, streamingText]);

  const send = useCallback(async () => {
    const msg = inputRef.current?.value.trim();
    if (!msg || busy) return;
    inputRef.current!.value = "";
    const title = !activeThreadId ? (titleRef.current?.value.trim() || undefined) : undefined;
    setHistory(prev => [...prev, { role: "user", content: msg }]);
    setBusy(true);
    setStreamingText("");
    let full = "";
    try {
      const result = await sendChatStream(activeThreadId, msg, (chunk) => {
        full += chunk;
        setStreamingText(full);
      }, title ? { title } : undefined);
      if (!activeThreadId && attachedIssue) {
        await attachThread(result.threadId, attachedIssue).catch(() => {});
      }
      setHistory(prev => [...prev, { role: "ace", content: full }]);
      setStreamingText(null);
      setActiveThreadId(result.threadId);
      setBusy(false);
      onSent(result.threadId);
    } catch (e: any) {
      setHistory(prev => [...prev, { role: "ace", content: `Error: ${(e as Error).message}` }]);
      setStreamingText(null);
      setBusy(false);
    }
  }, [busy, onSent, attachedIssue, activeThreadId]);

  const micClass = `chat-reply-mic${recording ? " recording" : ""}${transcribing ? " transcribing" : ""}`;

  return (
    <div class="detail-panel">
      <div class="detail-header">
        <span class="status-dot active pulse" />
        <input
          ref={titleRef}
          class="draft-title-input"
          type="text"
          placeholder="New message"
          autoComplete="off"
          disabled={busy || !!activeThreadId}
        />
        <button class="draft-close" onClick={onClose}><X size={16} /></button>
      </div>
      <div class="detail-log" ref={logRef}>
        {history.length === 0 && streamingText === null && (
          <div class="sess-none">Type a message to start a conversation with Ace.</div>
        )}
        {history.map((msg, i) => (
          msg.role === "user" ? (
            <div key={i} class="chat-msg chat-msg-user">
              <div class="chat-bubble">{msg.content}</div>
            </div>
          ) : (
            <div key={i} class="chat-msg chat-msg-ace">
              <div class="chat-bubble chat-bubble-md" dangerouslySetInnerHTML={{ __html: renderMarkdown(msg.content) }} />
            </div>
          )
        ))}
        {streamingText !== null && (
          <div class="chat-msg chat-msg-ace">
            <div
              class="chat-bubble chat-bubble-md"
              dangerouslySetInnerHTML={{ __html: streamingText ? renderMarkdown(streamingText) : '<span class="chat-thinking">Thinking\u2026</span>' }}
            />
          </div>
        )}
        {status && <div class="chat-waiting">{status}</div>}
      </div>
      <div class="detail-input">
        <input
          ref={inputRef}
          class="chat-reply-input"
          type="text"
          placeholder={activeThreadId ? "Reply..." : "Message Ace..."}
          autoComplete="off"
          onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } }}
          disabled={busy}
          autoFocus
        />
        <button class={micClass} onClick={toggle} title="Tap to record"><Mic size={16} /></button>
        <button class="chat-reply-send" onClick={send} disabled={busy}><SendHorizontal size={16} /></button>
      </div>
    </div>
  );
}

function ChatInput({ threadId, recognizer, onSent }: { threadId: string; recognizer: SpeechRecognizer; onSent: () => void }) {
  const inputRef = useRef<HTMLInputElement>(null);
  const [busy, setBusy] = useState(false);
  const [status, setStatus] = useState<string | null>(null);

  const onTranscribed = useCallback((text: string) => {
    if (inputRef.current) {
      inputRef.current.value += (inputRef.current.value ? " " : "") + text;
      inputRef.current.focus();
    }
  }, []);

  const { recording, transcribing, toggle } = useSpeechRecognizer(recognizer, { onResult: onTranscribed, onStatus: setStatus });

  const send = useCallback(async () => {
    const msg = inputRef.current?.value.trim();
    if (!msg || busy) return;
    inputRef.current!.value = "";
    setBusy(true);
    try {
      await sendChatStream(threadId, msg, () => {});
      onSent();
    } catch {}
    setBusy(false);
  }, [threadId, busy, onSent]);

  const micClass = `chat-reply-mic${recording ? " recording" : ""}${transcribing ? " transcribing" : ""}`;

  return (
    <div class="detail-input">
      <input
        ref={inputRef}
        class="chat-reply-input"
        type="text"
        placeholder="Reply..."
        autoComplete="off"
        onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } }}
        disabled={busy}
      />
      <button class={micClass} onClick={toggle} title="Tap to record"><Mic size={16} /></button>
      <button class="chat-reply-send" onClick={send} disabled={busy}><SendHorizontal size={16} /></button>
      {status && <div class="chat-waiting">{status}</div>}
      {busy && !status && <div class="chat-waiting">Sending...</div>}
    </div>
  );
}

const COLLAPSIBLE = new Set(["TOOL RESULT", "SESSION START"]);

function isCollapsible(type: string): boolean {
  return COLLAPSIBLE.has(type) || /^TOOL CALL/.test(type);
}

function LogEntry({ entry }: { entry: SessionEntry }) {
  const [open, setOpen] = useState(false);

  if (entry.type === "USER") {
    return (
      <div class="chat-msg chat-msg-user">
        <div class="chat-bubble">{entry.content}</div>
        <span class="chat-ts">{fmtSessTs(entry.ts)}</span>
      </div>
    );
  }

  if (entry.type === "TEXT") {
    return (
      <div class="chat-msg chat-msg-ace">
        <div class="chat-bubble chat-bubble-md" dangerouslySetInnerHTML={{ __html: renderMarkdown(entry.content) }} />
        <span class="chat-ts">{fmtSessTs(entry.ts)}</span>
      </div>
    );
  }

  const collapsed = isCollapsible(entry.type);
  const showContent = /^RESULT/.test(entry.type);

  return (
    <div class="sess-entry">
      <div
        class={`sess-header${collapsed ? " sess-collapsible" : ""}`}
        onClick={collapsed ? () => setOpen(!open) : undefined}
      >
        {collapsed && <span class={`sess-chevron${open ? " open" : ""}`}><ChevronRight size={12} /></span>}
        <span class={`sess-type ${typeClass(entry.type)}`}>{formatTypeLabel(entry.type)}</span>
        <span class="sess-ts">{fmtSessTs(entry.ts)}</span>
        {collapsed && !open && entry.content && (
          <span class="sess-preview">{entry.content.slice(0, 60)}{entry.content.length > 60 ? "..." : ""}</span>
        )}
      </div>
      {showContent && (
        <div class="sess-body">{entry.content}</div>
      )}
      {collapsed && open && (
        <div class="sess-body sess-collapsed-body">{entry.content}</div>
      )}
    </div>
  );
}
