"use client";

import { useRouter } from "next/navigation";
import { Archive, Plus, Trash2 } from "lucide-react";
import { useEffect, useRef, useState, useTransition } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { ShareButton } from "@/components/share-button";
import type { ListDetailDto, ListItemDto } from "../server/queries";
import { getList } from "../server/queries";
import {
  addItem,
  archiveList,
  deleteItem,
  renameList,
  toggleItem,
  updateItem,
} from "../server/actions";

export function ListDetail({ initialList }: { initialList: ListDetailDto }) {
  const router = useRouter();
  const [list, setList] = useState(initialList);
  const [draft, setDraft] = useState("");
  const [isPending, startTransition] = useTransition();
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    const events = new EventSource(`/api/lists/${initialList.id}/events`);
    events.onmessage = () => {
      getList(initialList.id)
        .then(setList)
        .catch(() => undefined);
    };
    return () => events.close();
  }, [initialList.id]);

  function submitItem() {
    const text = draft.trim();
    if (!text) return;

    startTransition(async () => {
      const next = await addItem({ listId: list.id, text });
      setList(next);
      setDraft("");
      requestAnimationFrame(() => inputRef.current?.focus());
    });
  }

  function setItemDone(item: ListItemDto, done: boolean) {
    setList((current) => ({
      ...current,
      items: current.items.map((row) => (row.id === item.id ? { ...row, done } : row)),
    }));
    startTransition(async () => {
      setList(await toggleItem({ id: item.id, done }));
    });
  }

  function editItemText(item: ListItemDto, text: string) {
    setList((current) => ({
      ...current,
      items: current.items.map((row) => (row.id === item.id ? { ...row, text } : row)),
    }));
  }

  function commitItemText(item: ListItemDto) {
    if (!item.text.trim()) return;
    startTransition(async () => {
      setList(await updateItem({ id: item.id, text: item.text }));
    });
  }

  function removeItem(item: ListItemDto) {
    setList((current) => ({
      ...current,
      items: current.items.filter((row) => row.id !== item.id),
    }));
    startTransition(async () => {
      setList(await deleteItem({ id: item.id }));
    });
  }

  function commitListName() {
    if (!list.name.trim()) return;
    startTransition(async () => {
      await renameList({ id: list.id, name: list.name });
    });
  }

  function archiveCurrentList() {
    startTransition(async () => {
      await archiveList({ id: list.id });
      router.push("/lists");
    });
  }

  const openItems = list.items.filter((i) => !i.done);
  const doneItems = list.items.filter((i) => i.done);

  return (
    <div className="mx-auto grid w-full max-w-3xl gap-4">
      <header className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
        <div className="min-w-0 flex items-center gap-3 flex-1">
          <Input
            aria-label="List name"
            className="serif h-auto border-transparent !bg-transparent px-0 text-[22px] font-medium tracking-tight shadow-none focus-visible:border-transparent focus-visible:ring-0"
            value={list.name}
            onChange={(event) => setList({ ...list, name: event.target.value })}
            onBlur={commitListName}
          />
          <span className="badge whitespace-nowrap shrink-0">
            {list.type} · {list.openCount} open
          </span>
        </div>
        <div className="flex items-center gap-2">
          <ShareButton entityType="lists.list" entityId={list.id} canWrite={true} />
          <Button variant="outline" size="sm" onClick={archiveCurrentList} disabled={isPending}>
            <Archive className="size-3.5" />
            Archive
          </Button>
        </div>
      </header>

      <div
        className="rounded-[var(--r-lg)] border-[0.5px] bg-[var(--card)] shadow-[var(--shadow-1)] overflow-hidden"
        style={{ borderColor: "var(--hair)" }}
      >
        <form
          className="flex items-center gap-2 px-[14px] py-3 border-b-[0.5px]"
          style={{ borderColor: "var(--hair)" }}
          onSubmit={(event) => {
            event.preventDefault();
            submitItem();
          }}
        >
          <Plus className="size-4 text-[var(--ink-mute)]" />
          <Input
            ref={inputRef}
            aria-label="Add item"
            autoFocus
            placeholder={list.type === "shopping" ? "Add to list…" : "Add a task…"}
            className="!border-0 !bg-transparent !shadow-none focus-visible:!ring-0 px-0 h-7"
            value={draft}
            onChange={(event) => setDraft(event.target.value)}
          />
          <span className="kbd">↵</span>
        </form>

        {openItems.length === 0 && doneItems.length === 0 && (
          <div className="muted px-[14px] py-8 text-center text-[13px]">Nothing here yet.</div>
        )}

        {openItems.map((item) => (
          <ListItemRow
            key={item.id}
            item={item}
            onToggle={(done) => setItemDone(item, done)}
            onEdit={(text) => editItemText(item, text)}
            onCommit={() => commitItemText(item)}
            onRemove={() => removeItem(item)}
          />
        ))}

        {doneItems.length > 0 && (
          <>
            <div className="eyebrow px-[14px] pt-3 pb-[6px]">Done · {doneItems.length}</div>
            {doneItems.map((item) => (
              <ListItemRow
                key={item.id}
                item={item}
                onToggle={(done) => setItemDone(item, done)}
                onEdit={(text) => editItemText(item, text)}
                onCommit={() => commitItemText(item)}
                onRemove={() => removeItem(item)}
              />
            ))}
          </>
        )}
      </div>
    </div>
  );
}

function ListItemRow({
  item,
  onToggle,
  onEdit,
  onCommit,
  onRemove,
}: {
  item: ListItemDto;
  onToggle: (done: boolean) => void;
  onEdit: (text: string) => void;
  onCommit: () => void;
  onRemove: () => void;
}) {
  const swipeOrigin = useRef<{ x: number; y: number } | null>(null);
  const isScrolling = useRef(false);
  const [swipeX, setSwipeX] = useState(0);

  const THRESHOLD = 80; // px to commit the swipe action
  const MAX_SWIPE = 100; // px maximum visual displacement

  function handlePointerDown(e: React.PointerEvent<HTMLDivElement>) {
    if ((e.target as HTMLElement).closest("button, input")) return;
    e.currentTarget.setPointerCapture(e.pointerId);
    swipeOrigin.current = { x: e.clientX, y: e.clientY };
    isScrolling.current = false;
    setSwipeX(0);
  }

  function handlePointerMove(e: React.PointerEvent<HTMLDivElement>) {
    if (!swipeOrigin.current) return;
    const dx = e.clientX - swipeOrigin.current.x;
    const dy = Math.abs(e.clientY - swipeOrigin.current.y);
    // If the gesture is primarily vertical, treat as scroll and abort swipe.
    if (!isScrolling.current && dy > Math.abs(dx) && dy > 8) {
      isScrolling.current = true;
      setSwipeX(0);
      return;
    }
    if (isScrolling.current) return;
    setSwipeX(Math.max(-MAX_SWIPE, Math.min(MAX_SWIPE, dx)));
  }

  function handlePointerUp(e: React.PointerEvent<HTMLDivElement>) {
    if (swipeOrigin.current && !isScrolling.current) {
      const dx = e.clientX - swipeOrigin.current.x;
      if (dx > THRESHOLD) onToggle(!item.done); // right → complete / uncomplete
      if (dx < -THRESHOLD) onRemove(); // left  → delete
    }
    swipeOrigin.current = null;
    setSwipeX(0);
  }

  function handlePointerCancel() {
    swipeOrigin.current = null;
    setSwipeX(0);
  }

  // Background tint that deepens as the swipe approaches the threshold.
  const swipeProgress = Math.abs(swipeX) / MAX_SWIPE;
  const swipeBg =
    swipeX > 16
      ? `color-mix(in oklab, var(--ok) ${swipeProgress * 55}%, var(--card))`
      : swipeX < -16
        ? `color-mix(in oklab, var(--bad) ${swipeProgress * 55}%, var(--card))`
        : undefined;

  return (
    <div
      className="relative overflow-hidden border-b-[0.5px] last:border-b-0 group/row"
      style={{ borderColor: "var(--hair)", background: swipeBg, touchAction: "pan-y" }}
      onPointerDown={handlePointerDown}
      onPointerMove={handlePointerMove}
      onPointerUp={handlePointerUp}
      onPointerCancel={handlePointerCancel}
    >
      {/* Swipe affordance icons revealed behind the sliding content */}
      <div
        className="absolute inset-y-0 left-3 flex items-center"
        aria-hidden
        style={{ opacity: Math.max(0, swipeX - 16) / (MAX_SWIPE - 16), color: "var(--ok)" }}
      >
        <svg
          width="18"
          height="18"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          strokeWidth="2.4"
          strokeLinecap="round"
          strokeLinejoin="round"
        >
          <path d="M5 12l5 5L20 7" />
        </svg>
      </div>
      <div
        className="absolute inset-y-0 right-3 flex items-center"
        aria-hidden
        style={{ opacity: Math.max(0, -swipeX - 16) / (MAX_SWIPE - 16), color: "var(--bad)" }}
      >
        <Trash2 size={16} />
      </div>

      {/* Sliding content row */}
      <div
        className={`flex items-center gap-3 px-[14px] py-[10px] hover:bg-[var(--shade)] ${item.done ? "checked" : ""}`}
        style={{
          transform: `translateX(${swipeX}px)`,
          transition: swipeX === 0 ? "transform 0.22s ease-out" : "none",
        }}
      >
        <button
          type="button"
          aria-label={`Complete ${item.text}`}
          aria-pressed={item.done}
          className={`checkbox ${item.done ? "on" : ""}`}
          onClick={() => onToggle(!item.done)}
        >
          {item.done && (
            <svg
              width="12"
              height="12"
              viewBox="0 0 24 24"
              fill="none"
              stroke="white"
              strokeWidth="2.4"
              strokeLinecap="round"
              strokeLinejoin="round"
            >
              <path d="M5 12l5 5L20 7" />
            </svg>
          )}
        </button>
        <Input
          aria-label={`${item.text} text`}
          className={`!border-0 !bg-transparent !shadow-none focus-visible:!ring-0 row-text flex-1 px-0 h-7 text-[13.5px] ${item.done ? "line-through text-[var(--ink-faint)]" : ""}`}
          value={item.text}
          onChange={(event) => onEdit(event.target.value)}
          onBlur={onCommit}
        />
        {/* Always visible on touch (list-row-delete makes opacity-0 override to 1 on mobile) */}
        <Button
          size="icon-sm"
          variant="ghost"
          aria-label={`Delete ${item.text}`}
          onClick={onRemove}
          className="list-row-delete opacity-0 group-hover/row:opacity-100 transition-opacity"
        >
          <Trash2 className="size-3.5" />
        </Button>
      </div>
    </div>
  );
}
