"use client";

import {
	closestCorners,
	DndContext,
	type DragEndEvent,
	type DragOverEvent,
	DragOverlay,
	type DragStartEvent,
	KeyboardSensor,
	PointerSensor,
	useSensor,
	useSensors,
} from "@dnd-kit/core";
import {
	arrayMove,
	horizontalListSortingStrategy,
	SortableContext,
	sortableKeyboardCoordinates,
} from "@dnd-kit/sortable";
import { cn } from "@mdxui/primitives/lib/utils";
import * as React from "react";
import { KanbanCardComponent } from "./kanban-card";
import { KanbanColumnComponent } from "./kanban-column";
import type { KanbanBoardProps, KanbanCard, KanbanColumn } from "./types";

export function KanbanBoard({
	columns: initialColumns,
	onCardMove,
	onCardClick,
	onAddCard,
	onColumnReorder,
	draggable = true,
	showAddButton = true,
	className,
}: KanbanBoardProps) {
	const [columns, setColumns] = React.useState(initialColumns);
	const [activeCard, setActiveCard] = React.useState<{
		card: KanbanCard;
		columnId: string;
	} | null>(null);
	const [activeColumn, setActiveColumn] = React.useState<KanbanColumn | null>(
		null,
	);

	React.useEffect(() => {
		setColumns(initialColumns);
	}, [initialColumns]);

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

	const handleDragStart = (event: DragStartEvent) => {
		const { active } = event;
		const activeData = active.data.current;

		if (activeData?.type === "card") {
			setActiveCard({
				card: activeData.card,
				columnId: activeData.columnId,
			});
		} else if (activeData?.type === "column") {
			setActiveColumn(activeData.column);
		}
	};

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

		const activeData = active.data.current;
		const overData = over.data.current;

		if (activeData?.type !== "card" || !overData) return;

		const activeColumnId = activeData.columnId;
		const overColumnId =
			overData.type === "column" ? overData.column.id : overData.columnId;

		if (activeColumnId === overColumnId) return;

		setColumns((prevColumns) => {
			const activeColumnIndex = prevColumns.findIndex(
				(col) => col.id === activeColumnId,
			);
			const overColumnIndex = prevColumns.findIndex(
				(col) => col.id === overColumnId,
			);

			if (activeColumnIndex === -1 || overColumnIndex === -1)
				return prevColumns;

			const activeColumn = prevColumns[activeColumnIndex];
			const overColumn = prevColumns[overColumnIndex];

			const activeCardIndex = activeColumn.cards.findIndex(
				(card) => card.id === active.id,
			);
			if (activeCardIndex === -1) return prevColumns;

			const newColumns = [...prevColumns];
			const [movedCard] = newColumns[activeColumnIndex].cards.splice(
				activeCardIndex,
				1,
			);

			const overCardIndex =
				overData.type === "card" ? overData.index : overColumn.cards.length;
			newColumns[overColumnIndex].cards.splice(overCardIndex, 0, movedCard);

			return newColumns;
		});
	};

	const handleDragEnd = (event: DragEndEvent) => {
		const { active, over } = event;
		setActiveCard(null);
		setActiveColumn(null);

		if (!over) return;

		const activeData = active.data.current;
		const overData = over.data.current;

		if (activeData?.type === "column" && overData?.type === "column") {
			const activeIndex = columns.findIndex((col) => col.id === active.id);
			const overIndex = columns.findIndex((col) => col.id === over.id);

			if (activeIndex !== overIndex) {
				const newColumns = arrayMove(columns, activeIndex, overIndex);
				setColumns(newColumns);
				onColumnReorder?.(newColumns);
			}
		} else if (activeData?.type === "card") {
			const activeColumnId = activeData.columnId;
			const overColumnId =
				overData?.type === "column" ? overData.column.id : overData?.columnId;

			if (!overColumnId) return;

			const activeColumnIndex = columns.findIndex(
				(col) => col.id === activeColumnId,
			);
			const overColumnIndex = columns.findIndex(
				(col) => col.id === overColumnId,
			);

			if (activeColumnIndex === -1 || overColumnIndex === -1) return;

			const activeColumn = columns[activeColumnIndex];
			const activeCardIndex = activeColumn.cards.findIndex(
				(card) => card.id === active.id,
			);

			if (activeCardIndex === -1) return;

			const overColumn = columns[overColumnIndex];
			const overCardIndex =
				overData?.type === "card" ? overData.index : overColumn.cards.length;

			if (activeColumnId === overColumnId) {
				if (activeCardIndex !== overCardIndex) {
					setColumns((prevColumns) => {
						const newColumns = [...prevColumns];
						newColumns[activeColumnIndex].cards = arrayMove(
							newColumns[activeColumnIndex].cards,
							activeCardIndex,
							overCardIndex,
						);
						return newColumns;
					});
				}
			} else {
				onCardMove?.(
					active.id as string,
					activeColumnId,
					overColumnId,
					overCardIndex,
				);
			}
		}
	};

	const columnIds = columns.map((col) => col.id);

	return (
		<DndContext
			sensors={sensors}
			collisionDetection={closestCorners}
			onDragStart={handleDragStart}
			onDragOver={handleDragOver}
			onDragEnd={handleDragEnd}
		>
			<div className={cn("flex h-full gap-4 overflow-x-auto pb-4", className)}>
				<SortableContext
					items={columnIds}
					strategy={horizontalListSortingStrategy}
				>
					{columns.map((column) => (
						<KanbanColumnComponent
							key={column.id}
							column={column}
							draggable={draggable}
							showAddButton={showAddButton}
							onCardClick={onCardClick}
							onAddCard={onAddCard}
						/>
					))}
				</SortableContext>
			</div>

			<DragOverlay>
				{activeCard && (
					<KanbanCardComponent
						card={activeCard.card}
						columnId={activeCard.columnId}
						isDragging
					/>
				)}
				{activeColumn && (
					<div className="w-80 rounded-lg border bg-card p-4 opacity-50">
						<h3 className="font-semibold">{activeColumn.title}</h3>
					</div>
				)}
			</DragOverlay>
		</DndContext>
	);
}
