import {
    DndContext,
    PointerSensor,
    closestCorners,
    useSensor,
    useSensors,
} from '@dnd-kit/core';
import type { DragEndEvent, DragOverEvent, DragStartEvent } from '@dnd-kit/core';
import { arrayMove } from '@dnd-kit/sortable';
import { useState } from 'react';
import type {
    BoardWithColumnsResponse,
    CardResponse,
    ColumnWithCardsResponse,
} from '@/client';
import { cardMove } from '@/client';
import { KanbanColumn } from './KanbanColumn';

interface KanbanBoardProps {
    board: BoardWithColumnsResponse;
    onCardMoved?: () => void;
}

export function KanbanBoard({ board, onCardMoved }: KanbanBoardProps) {
    const [columns, setColumns] = useState<ColumnWithCardsResponse[]>(board.columns);
    const [, setActiveCard] = useState<CardResponse | null>(null);

    const sensors = useSensors(
        useSensor(PointerSensor, { activationConstraint: { distance: 8 } })
    );

    const findColumnByCardId = (cardId: string): number => {
        return columns.findIndex((col) => col.cards.some((c) => c.id === cardId));
    };

    const handleDragStart = (event: DragStartEvent) => {
        const { active } = event;
        const card = active.data.current?.card as CardResponse | undefined;
        setActiveCard(card ?? null);
    };

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

        const activeId = active.id as string;
        const overId = over.id as string;
        const overData = over.data.current;

        const activeColIdx = findColumnByCardId(activeId);
        if (activeColIdx === -1) return;

        // Dragging over a column
        if (overData?.type === 'Column') {
            const overColIdx = columns.findIndex((c) => c.column.id === overId);
            if (activeColIdx !== overColIdx) {
                setColumns((prev) => {
                    const activeCol = prev[activeColIdx];
                    const overCol = prev[overColIdx];
                    const cardIdx = activeCol.cards.findIndex((c) => c.id === activeId);
                    const [card] = activeCol.cards.splice(cardIdx, 1);
                    const newCols = [...prev];
                    newCols[activeColIdx] = { ...activeCol, cards: activeCol.cards.filter((c) => c.id !== activeId) };
                    newCols[overColIdx] = { ...overCol, cards: [...overCol.cards, card] };
                    return newCols;
                });
            }
            return;
        }

        // Dragging over another card
        if (overData?.type === 'Card') {
            const overColIdx = findColumnByCardId(overId);
            if (activeColIdx === overColIdx) {
                const col = columns[activeColIdx];
                const oldIdx = col.cards.findIndex((c) => c.id === activeId);
                const newIdx = col.cards.findIndex((c) => c.id === overId);
                if (oldIdx !== newIdx) {
                    setColumns((prev) => {
                        const newCards = arrayMove(col.cards, oldIdx, newIdx);
                        const newCols = [...prev];
                        newCols[activeColIdx] = { ...col, cards: newCards };
                        return newCols;
                    });
                }
            } else {
                setColumns((prev) => {
                    const activeCol = prev[activeColIdx];
                    const overCol = prev[overColIdx];
                    const oldIdx = activeCol.cards.findIndex((c) => c.id === activeId);
                    const [card] = activeCol.cards.splice(oldIdx, 1);
                    const overCardIdx = overCol.cards.findIndex((c) => c.id === overId);
                    overCol.cards.splice(overCardIdx, 0, card);
                    const newCols = [...prev];
                    newCols[activeColIdx] = { ...activeCol, cards: [...activeCol.cards] };
                    newCols[overColIdx] = { ...overCol, cards: [...overCol.cards] };
                    return newCols;
                });
            }
        }
    };

    const handleDragEnd = async (event: DragEndEvent) => {
        setActiveCard(null);
        const { active, over } = event;
        if (!over || active.id === over.id) return;

        const activeId = active.id as string;
        const overId = over.id as string;
        const overData = over.data.current;

        let targetColumnId = overId;
        let position = 0;

        if (overData?.type === 'Column') {
            const colIdx = columns.findIndex((c) => c.column.id === overId);
            position = columns[colIdx]?.cards.length ?? 0;
        } else if (overData?.type === 'Card') {
            targetColumnId = overData?.card?.column ?? overId;
            const colIdx = columns.findIndex((c) => c.column.id === (overData?.card?.column ?? overId));
            if (colIdx !== -1) {
                const cardIdx = columns[colIdx].cards.findIndex((c) => c.id === overId);
                position = cardIdx;
            }
        }

        try {
            await cardMove({
                path: { project_name: board.board.project, card_id: activeId },
                body: {
                    target_column_id: targetColumnId,
                    position,
                },
            });
            onCardMoved?.();
        } catch {
            // On error, data will refresh from parent
        }
    };

    return (
        <DndContext
            sensors={sensors}
            collisionDetection={closestCorners}
            onDragStart={handleDragStart}
            onDragOver={handleDragOver}
            onDragEnd={handleDragEnd}
        >
            <div className="flex h-full gap-4 overflow-x-auto p-4">
                {columns.map((colData) => (
                    <KanbanColumn
                        key={colData.column.id}
                        columnData={colData}
                        onCardAdded={onCardMoved}
                    />
                ))}
                {columns.length === 0 && (
                    <div className="flex flex-1 items-center justify-center text-muted-foreground">
                        No columns yet. Create a column to get started.
                    </div>
                )}
            </div>
        </DndContext>
    );
}
