import { useState, useRef, useCallback, useEffect } from "preact/hooks";
import type { ThreadData } from "../../lib/types";
import { fmtDur, durClass, fmtTime, statusOf } from "../../lib/format";
import StatusBadge from "./StatusBadge";
import { Undo2 } from "lucide-preact";

function fmtK(n: number): string {
  return n >= 1000 ? (n / 1000).toFixed(1) + "k" : String(n);
}

interface Props {
  thread: ThreadData;
  selected: boolean;
  onClick: () => void;
  onChangeStatus?: (newStatus: string) => void;
  onDelete?: () => void;
  onArchive?: () => void;
  onDebug?: () => Promise<void>;
}

const SWIPE_THRESHOLD = 80;
const UNDO_TIMEOUT = 4000;

type SwipeAction = null | "deleted" | "archived";

export default function ThreadCard({ thread, selected, onClick, onChangeStatus, onDelete, onArchive, onDebug }: Props) {
  const isActive = thread.status === "processing";
  const st = statusOf(thread);
  const dur = (thread.endedAt || Date.now()) - thread.startedAt;

  const [debugging, setDebugging] = useState(false);
  const [swipeX, setSwipeX] = useState(0);
  const [swipeAction, setSwipeAction] = useState<SwipeAction>(null);
  const [pendingAction, setPendingAction] = useState<SwipeAction>(null);
  const dragStart = useRef({ x: 0, y: 0, dragging: false, decided: false });
  const undoTimer = useRef<ReturnType<typeof setTimeout> | null>(null);

  const canSwipe = !!onDelete || !!onArchive;
  const onDeleteRef = useRef(onDelete);
  onDeleteRef.current = onDelete;
  const onArchiveRef = useRef(onArchive);
  onArchiveRef.current = onArchive;

  const onPointerDown = useCallback((e: PointerEvent) => {
    if (!canSwipe) return;
    dragStart.current = { x: e.clientX, y: e.clientY, dragging: false, decided: false };
    (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
  }, [canSwipe]);

  const onPointerMove = useCallback((e: PointerEvent) => {
    if (!canSwipe) return;
    if (!(e.currentTarget as HTMLElement).hasPointerCapture(e.pointerId)) return;
    const dx = e.clientX - dragStart.current.x;
    const dy = e.clientY - dragStart.current.y;
    if (!dragStart.current.decided) {
      if (Math.abs(dx) < 5 && Math.abs(dy) < 5) return;
      dragStart.current.decided = true;
      if (Math.abs(dy) > Math.abs(dx)) {
        (e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId);
        return;
      }
      dragStart.current.dragging = true;
    }
    if (dragStart.current.dragging) {
      const clamped = onDelete && dx < 0 ? dx : onArchive && dx > 0 ? dx : 0;
      setSwipeX(clamped);
    }
  }, [canSwipe, onDelete, onArchive]);

  const onPointerUp = useCallback((e: PointerEvent) => {
    if (!canSwipe) return;
    if (dragStart.current.dragging) {
      if (swipeX < -SWIPE_THRESHOLD && onDelete) {
        setSwipeAction("deleted");
        setPendingAction("deleted");
      } else if (swipeX > SWIPE_THRESHOLD && onArchive) {
        setSwipeAction("archived");
        setPendingAction("archived");
      }
    }
    setSwipeX(0);
    dragStart.current = { x: 0, y: 0, dragging: false, decided: false };
  }, [canSwipe, swipeX, onDelete, onArchive]);

  const handleDebug = useCallback(async (e: Event) => {
    e.stopPropagation();
    if (debugging || !onDebug) return;
    setDebugging(true);
    try { await onDebug(); } catch {}
    setDebugging(false);
  }, [debugging, onDebug]);

  const handleClick = useCallback((e: MouseEvent) => {
    if (dragStart.current.dragging) {
      e.preventDefault();
      e.stopPropagation();
      return;
    }
    onClick();
  }, [onClick]);

  useEffect(() => {
    if (!pendingAction) return;
    undoTimer.current = setTimeout(() => {
      if (pendingAction === "deleted") onDeleteRef.current?.();
      else if (pendingAction === "archived") onArchiveRef.current?.();
    }, UNDO_TIMEOUT);
    return () => { if (undoTimer.current) clearTimeout(undoTimer.current); };
  }, [pendingAction]);

  const handleUndo = useCallback((e: Event) => {
    e.stopPropagation();
    if (undoTimer.current) clearTimeout(undoTimer.current);
    setPendingAction(null);
    setSwipeAction(null);
  }, []);

  const title = thread.linearIssue ? (thread.linearTitle || thread.subject) : thread.subject;

  if (swipeAction) {
    const label = swipeAction === "deleted" ? "Deleted" : "Archived";
    return (
      <div class={`tcard tcard-${swipeAction}`}>
        <span class="tcard-action-label">{label}: {title}</span>
        <button class="tcard-undo-btn" onClick={handleUndo}><Undo2 size={14} /> Undo</button>
      </div>
    );
  }

  const style = swipeX !== 0
    ? { transform: `translateX(${swipeX}px)`, transition: "none" }
    : { transform: "translateX(0)", transition: "transform 0.2s" };

  return (
    <div class="tcard-swipe-wrap">
      {swipeX < 0 && <div class="tcard-swipe-bg tcard-swipe-delete">Delete</div>}
      {swipeX > 0 && <div class="tcard-swipe-bg tcard-swipe-archive">Archive</div>}
      <div
        class={`tcard${selected ? " selected" : ""}`}
        onClick={handleClick}
        onPointerDown={onPointerDown}
        onPointerMove={onPointerMove}
        onPointerUp={onPointerUp}
        style={{ ...style, touchAction: "pan-y" }}
      >
        <div class="tcard-hdr">
          <span class={`status-dot ${st}${isActive ? " pulse" : ""}`} />
          {thread.linearIssue && (
            <a
              class="linear-badge"
              href={thread.linearUrl || "#"}
              target="_blank"
              rel="noopener"
              onClick={(e) => e.stopPropagation()}
            >
              {thread.linearIssue}
            </a>
          )}
          <span class="tcard-title">{title}</span>
          <span class={`tcard-dur ${durClass(dur)}`}>{fmtDur(dur)}</span>
        </div>
        <div class="tcard-meta">
          <StatusBadge status={thread.status} onChangeStatus={onChangeStatus} onDelete={onDelete} hasLinear={!!thread.linearIssue} />
          {(thread.status === "stalled" || thread.status === "failed") && onDebug && (
            <button class="debug-btn" onClick={handleDebug} disabled={debugging}>
              {debugging ? "..." : "Debug"}
            </button>
          )}
          <span class="ch-tag">via {thread.channel}</span>
          <span>{fmtTime(thread.startedAt)}</span>
          {thread.tokIn != null && thread.tokOut != null && thread.cost != null && (
            <span class="usage-tag">{fmtK(thread.tokIn)}in/{fmtK(thread.tokOut)}out · ${thread.cost.toFixed(4)}</span>
          )}
        </div>
      </div>
    </div>
  );
}
