import React, { useState, useRef, useEffect, useCallback } from "react";
import { Plus } from "lucide-react";
import { AnimatePresence, motion } from "framer-motion";
import { OverlayScrollbarsComponent, type OverlayScrollbarsComponentRef } from "overlayscrollbars-react";
import { staggerContainer } from "@/lib/motion";
import {
  DndContext,
  DragOverlay,
  closestCorners,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  type DragStartEvent,
  type DragOverEvent,
  type DragEndEvent,
} from "@dnd-kit/core";
import {
  SortableContext,
  horizontalListSortingStrategy,
} from "@dnd-kit/sortable";
import { Button } from "@/components/ui/button";
import { useBoardStore } from "@/stores/board-store";
import { KanbanColumn } from "@/components/board/KanbanColumn";
import {
  CardOverlay,
  ColumnOverlay,
} from "@/components/board/DragOverlayContent";
import { CardDetailModal } from "@/components/card-detail/CardDetailModal";
import { FilterBar, type FilterState, EMPTY_FILTER, isFilterActive } from "@/components/board/FilterBar";
import { useKeyboardNavigation } from "@/hooks/useKeyboardNavigation";
import type { Board } from "@/types/board";

function findColumnByCardId(board: Board, cardId: string) {
  return board.columns.find((col) => col.cardIds.includes(cardId));
}

function getBoardBackground(board: Board): React.CSSProperties {
  const bg = board.settings.background;
  if (bg === "dots") {
    return {
      backgroundImage: `radial-gradient(circle, currentColor 1px, transparent 1px)`,
      backgroundSize: "20px 20px",
      color: "oklch(50% 0 0 / 5%)",
    };
  }
  if (bg === "grid") {
    return {
      backgroundImage: `linear-gradient(currentColor 1px, transparent 1px), linear-gradient(90deg, currentColor 1px, transparent 1px)`,
      backgroundSize: "24px 24px",
      color: "oklch(50% 0 0 / 5%)",
    };
  }
  if (bg === "gradient") {
    return {
      background: `linear-gradient(135deg, ${board.color}08, ${board.color}03, transparent)`,
    };
  }
  return {};
}

export function BoardView() {
  const board = useBoardStore((s) => s.board);
  const addColumn = useBoardStore((s) => s.addColumn);
  const moveCard = useBoardStore((s) => s.moveCard);
  const moveColumn = useBoardStore((s) => s.moveColumn);

  const [selectedCardId, setSelectedCardId] = useState<string | null>(null);
  const [showFilterBar, setShowFilterBar] = useState(false);
  const [filters, setFilters] = useState<FilterState>(EMPTY_FILTER);
  const { focusedCardId, setFocusedCardId } = useKeyboardNavigation(board, handleCardClick);
  const [addingColumn, setAddingColumn] = useState(false);
  const [newColumnTitle, setNewColumnTitle] = useState("");
  const inputRef = useRef<HTMLInputElement>(null);
  const osRef = useRef<OverlayScrollbarsComponentRef>(null);

  // Track columns that existed on initial render (for stagger vs instant appearance)
  const initialColumnIds = useRef<Set<string> | null>(null);
  if (initialColumnIds.current === null && board) {
    initialColumnIds.current = new Set(board.columns.map((c) => c.id));
  }

  function handleCardClick(cardId: string) {
    setSelectedCardId(cardId);
    setFocusedCardId(null);
  }

  function filterCards(cardIds: string[]): string[] {
    if (!isFilterActive(filters) || !board) return cardIds;
    return cardIds.filter((id) => {
      const card = board.cards[id];
      if (!card) return false;
      if (filters.text && !card.title.toLowerCase().includes(filters.text.toLowerCase())) return false;
      if (filters.labels.length > 0 && !filters.labels.some((l) => card.labels.includes(l))) return false;
      if (filters.priority !== "all" && card.priority !== filters.priority) return false;
      if (filters.dueDate !== "all") {
        const now = new Date();
        const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
        if (filters.dueDate === "none" && card.dueDate != null) return false;
        if (filters.dueDate === "overdue" && (!card.dueDate || new Date(card.dueDate) >= today)) return false;
        if (filters.dueDate === "today") {
          if (!card.dueDate) return false;
          const d = new Date(card.dueDate);
          if (d.toDateString() !== today.toDateString()) return false;
        }
        if (filters.dueDate === "week") {
          if (!card.dueDate) return false;
          const d = new Date(card.dueDate);
          const weekEnd = new Date(today);
          weekEnd.setDate(weekEnd.getDate() + 7);
          if (d < today || d > weekEnd) return false;
        }
      }
      return true;
    });
  }

  // Keyboard shortcut: "/" to open filter bar
  useEffect(() => {
    function handleKey(e: KeyboardEvent) {
      if (e.key === "/" && !e.ctrlKey && !e.metaKey) {
        const tag = (e.target as HTMLElement).tagName;
        if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
        e.preventDefault();
        setShowFilterBar(true);
      }
    }
    document.addEventListener("keydown", handleKey);
    return () => document.removeEventListener("keydown", handleKey);
  }, []);

  // Listen for toggle-filter-bar custom event from TopBar
  useEffect(() => {
    function handleToggleFilter() {
      setShowFilterBar((prev) => !prev);
    }
    document.addEventListener("toggle-filter-bar", handleToggleFilter);
    return () => document.removeEventListener("toggle-filter-bar", handleToggleFilter);
  }, []);

  // Listen for custom event to open card detail from command palette
  useEffect(() => {
    function handleOpenCard(e: Event) {
      const detail = (e as CustomEvent<{ cardId: string }>).detail;
      if (detail?.cardId) {
        setSelectedCardId(detail.cardId);
      }
    }
    document.addEventListener("open-card-detail", handleOpenCard);
    return () => {
      document.removeEventListener("open-card-detail", handleOpenCard);
    };
  }, []);

  // Drag state
  const [activeId, setActiveId] = useState<string | null>(null);
  const [activeType, setActiveType] = useState<"card" | "column" | null>(null);

  // Sensors: PointerSensor with distance of 5px prevents accidental drags on click
  const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: { distance: 5 },
    }),
    useSensor(KeyboardSensor)
  );

  useEffect(() => {
    if (addingColumn && inputRef.current) {
      inputRef.current.focus();
    }
  }, [addingColumn]);

  function handleAddColumn() {
    const trimmed = newColumnTitle.trim();
    if (trimmed) {
      addColumn(trimmed);
      setNewColumnTitle("");
      inputRef.current?.focus();
      // Force OverlayScrollbars to detect the new content and scroll to show it
      setTimeout(() => {
        const instance = osRef.current?.osInstance();
        if (instance) {
          instance.update(true);
          const viewport = instance.elements().viewport;
          viewport.scrollTo({ left: viewport.scrollWidth, behavior: "smooth" });
        }
      }, 50);
    }
  }

  function handleKeyDown(e: React.KeyboardEvent) {
    if (e.key === "Enter") {
      handleAddColumn();
    } else if (e.key === "Escape") {
      setAddingColumn(false);
      setNewColumnTitle("");
    }
  }

  // --- Drag handlers ---

  // Debounce cross-column moves to prevent oscillation crashes
  const lastCrossColumnMoveRef = useRef<number>(0);

  const clearDragState = useCallback(() => {
    setActiveId(null);
    setActiveType(null);
    lastCrossColumnMoveRef.current = 0;
  }, []);

  const handleDragStart = useCallback((event: DragStartEvent) => {
    const { active } = event;
    const type = active.data.current?.type as "card" | "column" | undefined;
    setActiveId(active.id as string);
    setActiveType(type ?? null);
    lastCrossColumnMoveRef.current = 0;
  }, []);

  const handleDragOver = useCallback(
    (event: DragOverEvent) => {
      const { active, over } = event;
      if (!over) return;

      // Always read fresh state to avoid stale-closure bugs
      const currentBoard = useBoardStore.getState().board;
      if (!currentBoard) return;

      const activeType = active.data.current?.type;
      if (activeType !== "card") return;

      const activeCardId = active.id as string;
      const overId = over.id as string;
      if (overId === activeCardId) return;

      const activeColumn = findColumnByCardId(currentBoard, activeCardId);
      if (!activeColumn) return;

      let overColumn: ReturnType<typeof findColumnByCardId>;
      let overIndex: number;

      const overType = over.data.current?.type;
      if (overType === "card") {
        overColumn = findColumnByCardId(currentBoard, overId);
        if (!overColumn) return;
        overIndex = overColumn.cardIds.indexOf(overId);
      } else if (overType === "column") {
        const columnId = over.data.current?.columnId as string | undefined;
        if (columnId) {
          overColumn = currentBoard.columns.find((c) => c.id === columnId);
        } else {
          overColumn = currentBoard.columns.find((c) => c.id === overId);
        }
        if (!overColumn) return;
        overIndex = overColumn.cardIds.length;
      } else {
        return;
      }

      // Only move cross-column (within-column handled by sortable transforms + dragEnd)
      if (activeColumn.id === overColumn.id) return;

      // Debounce: prevent rapid oscillation between columns
      const now = Date.now();
      if (now - lastCrossColumnMoveRef.current < 100) return;
      lastCrossColumnMoveRef.current = now;

      moveCard(activeCardId, activeColumn.id, overColumn.id, overIndex);
    },
    [moveCard]
  );

  const handleDragEnd = useCallback(
    (event: DragEndEvent) => {
      try {
        const { active, over } = event;

        // Always read fresh state
        const currentBoard = useBoardStore.getState().board;
        if (!over || !currentBoard) return;

        const type = active.data.current?.type;

        if (type === "column") {
          const activeColumnId = active.id as string;
          const overColumnId = over.id as string;

          if (activeColumnId !== overColumnId) {
            const fromIndex = currentBoard.columns.findIndex(
              (c) => c.id === activeColumnId
            );
            const toIndex = currentBoard.columns.findIndex(
              (c) => c.id === overColumnId
            );
            if (fromIndex !== -1 && toIndex !== -1) {
              moveColumn(fromIndex, toIndex);
            }
          }
        } else if (type === "card") {
          const activeCardId = active.id as string;
          const overId = over.id as string;

          const activeColumn = findColumnByCardId(currentBoard, activeCardId);
          if (!activeColumn) return;

          const overType = over.data.current?.type;

          if (overType === "card") {
            const overColumn = findColumnByCardId(currentBoard, overId);
            if (!overColumn) return;

            if (activeColumn.id === overColumn.id) {
              const oldIndex = activeColumn.cardIds.indexOf(activeCardId);
              const newIndex = activeColumn.cardIds.indexOf(overId);
              if (oldIndex !== newIndex) {
                moveCard(activeCardId, activeColumn.id, activeColumn.id, newIndex);
              }
            }
          } else if (overType === "column") {
            const columnId = over.data.current?.columnId as string | undefined;
            const targetColumnId = columnId ?? (over.id as string);
            const targetColumn = currentBoard.columns.find(
              (c) => c.id === targetColumnId
            );

            if (targetColumn && activeColumn.id !== targetColumn.id) {
              moveCard(
                activeCardId,
                activeColumn.id,
                targetColumn.id,
                targetColumn.cardIds.length
              );
            }
          }
        }
      } finally {
        clearDragState();
      }
    },
    [moveCard, moveColumn, clearDragState]
  );

  const [announcement, setAnnouncement] = useState("");

  const handleDragEndWithAnnouncement = useCallback(
    (event: DragEndEvent) => {
      // Read board BEFORE handleDragEnd potentially modifies it
      const currentBoard = useBoardStore.getState().board;
      handleDragEnd(event);

      const { active, over } = event;
      if (over && currentBoard) {
        const type = active.data.current?.type;
        if (type === "card") {
          const card = currentBoard.cards[active.id as string];
          const targetCol = over.data.current?.type === "column"
            ? currentBoard.columns.find((c) => c.id === (over.data.current?.columnId ?? over.id))
            : findColumnByCardId(currentBoard, over.id as string);
          if (card && targetCol) {
            setAnnouncement(`Moved card "${card.title}" to ${targetCol.title}`);
          }
        } else if (type === "column") {
          const col = currentBoard.columns.find((c) => c.id === (active.id as string));
          if (col) {
            setAnnouncement(`Reordered column "${col.title}"`);
          }
        }
      }
    },
    [handleDragEnd]
  );

  if (!board) {
    return (
      <div className="flex h-full items-center justify-center text-pylon-text-secondary">
        <span className="font-mono text-sm">Loading board...</span>
      </div>
    );
  }

  // Get the active item for the drag overlay
  const activeCard =
    activeType === "card" && activeId ? board.cards[activeId] : null;
  const activeColumn =
    activeType === "column" && activeId
      ? board.columns.find((c) => c.id === activeId)
      : null;

  const columnIds = board.columns.map((c) => c.id);

  return (
    <>
    {/* Visually hidden live region for drag-and-drop announcements */}
    <div
      aria-live="polite"
      aria-atomic="true"
      className="sr-only"
    >
      {announcement}
    </div>
    <AnimatePresence>
      {showFilterBar && board && (
        <FilterBar
          filters={filters}
          onChange={setFilters}
          onClose={() => { setShowFilterBar(false); setFilters(EMPTY_FILTER); }}
          boardLabels={board.labels}
        />
      )}
    </AnimatePresence>
    <DndContext
      sensors={sensors}
      collisionDetection={closestCorners}
      onDragStart={handleDragStart}
      onDragOver={handleDragOver}
      onDragEnd={handleDragEndWithAnnouncement}
      onDragCancel={clearDragState}
    >
      <SortableContext
        items={columnIds}
        strategy={horizontalListSortingStrategy}
      >
        <OverlayScrollbarsComponent
          ref={osRef}
          className="h-full"
          options={{ scrollbars: { theme: "os-theme-pylon", autoHide: "scroll", autoHideDelay: 600, clickScroll: true }, overflow: { y: "hidden" } }}
          defer
        >
        <motion.div
          className="flex h-full"
          style={{ gap: `calc(1.5rem * var(--density-factor))`, padding: `calc(1.5rem * var(--density-factor))`, ...getBoardBackground(board) }}
          variants={staggerContainer(0.06)}
          initial="hidden"
          animate="visible"
        >
          <AnimatePresence>
            {board.columns.map((column) => (
              <KanbanColumn
                key={column.id}
                column={column}
                filteredCardIds={isFilterActive(filters) ? filterCards(column.cardIds) : undefined}
                focusedCardId={focusedCardId}
                onCardClick={handleCardClick}
                isNew={!initialColumnIds.current?.has(column.id)}
              />
            ))}
          </AnimatePresence>

          {/* Add column button / inline input */}
          <div className="shrink-0">
            {addingColumn ? (
              <div className="flex w-[280px] flex-col gap-2 rounded-lg bg-pylon-column p-3">
                <input
                  ref={inputRef}
                  value={newColumnTitle}
                  onChange={(e) => setNewColumnTitle(e.target.value)}
                  onKeyDown={handleKeyDown}
                  onBlur={() => {
                    if (!newColumnTitle.trim()) {
                      setAddingColumn(false);
                    }
                  }}
                  placeholder="Column title..."
                  className="h-8 rounded-md bg-pylon-surface px-3 text-sm text-pylon-text outline-none placeholder:text-pylon-text-secondary focus:ring-1 focus:ring-pylon-accent"
                />
                <div className="flex gap-2">
                  <Button size="sm" onClick={handleAddColumn}>
                    Add
                  </Button>
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={() => {
                      setAddingColumn(false);
                      setNewColumnTitle("");
                    }}
                    className="text-pylon-text-secondary"
                  >
                    Cancel
                  </Button>
                </div>
              </div>
            ) : (
              <Button
                variant="ghost"
                size="sm"
                onClick={() => setAddingColumn(true)}
                className="h-10 w-[280px] justify-start border border-dashed border-pylon-text-secondary/30 text-pylon-text-secondary hover:border-pylon-text-secondary/60 hover:text-pylon-text"
              >
                <Plus className="size-4" />
                Add column
              </Button>
            )}
          </div>
        </motion.div>
        </OverlayScrollbarsComponent>
      </SortableContext>

      {/* Drag overlay - renders a styled copy of the dragged item */}
      <DragOverlay dropAnimation={null}>
        <AnimatePresence>
          {activeCard ? (
            <CardOverlay key="card-overlay" card={activeCard} boardLabels={board.labels} />
          ) : activeColumn ? (
            <ColumnOverlay key="column-overlay" column={activeColumn} />
          ) : null}
        </AnimatePresence>
      </DragOverlay>
    </DndContext>

    <CardDetailModal
      cardId={selectedCardId}
      onClose={() => { setSelectedCardId(null); }}
    />
    </>
  );
}
