import { useCallback, useEffect, useRef, useState } from "react";
import { connect } from "react-redux";
import {
	closestCenter,
	DndContext,
	type DragEndEvent,
	type DragStartEvent,
	PointerSensor,
	useSensor,
	useSensors,
} from "@dnd-kit/core";
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";

import { structuredData } from "@ax/api";
import type { IRootState, IStructuredDataContent } from "@ax/types";
import { isReqOk } from "@ax/helpers";
import { Loader } from "@ax/components";

import Item from "./Item";
import { type SiteValue, useReference } from "../Context";

import * as S from "./style";

const ItemList = (props: IProps) => {
	const { items, currentSite, handleListDelete, handleListMove, site } = props;

	const { state, setState, setReorderElements } = useReference();
	const { fixed, selectedItems, sourceTitles, fullRelations } = state;
	const [activeId, setActiveId] = useState<number | null>(null);
	const [isLoading, setIsLoading] = useState(false);
	const abortControllerRef = useRef<AbortController | null>(null);

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

	const itemsIds: number[] = selectedItems.map((item) => item.id);

	const getParams = useCallback(() => {
		return {
			mode: "manual",
			fixed: items,
			fullRelations,
			site,
		};
	}, [items, fullRelations, site]);

	useEffect(() => {
		if (!items.length) return;

		// Cancel previous request if exists
		if (abortControllerRef.current) {
			abortControllerRef.current.abort();
		}

		abortControllerRef.current = new AbortController();
		const controller = abortControllerRef.current;

		const getItems = async () => {
			setIsLoading(true);
			try {
				const siteID = currentSite ?? "global";
				const params = getParams();
				const response = await structuredData.getDistributorContent(siteID, params);

				// Check if request was cancelled
				if (controller.signal.aborted) return;

				if (isReqOk(response?.status)) {
					const itemsID = response.data.map((item: IStructuredDataContent) => item.id);
					setState((state) => ({
						...state,
						selectedItems: response.data,
						fixed: itemsID,
					}));
				}
			} catch (error) {
				if (!controller.signal.aborted) {
					console.error("Error loading items:", error);
				}
			} finally {
				if (!controller.signal.aborted) {
					setIsLoading(false);
				}
			}
		};

		getItems();

		return () => {
			controller.abort();
		};
	}, [items, currentSite, getParams, setState]);

	const handleDelete = (item: IStructuredDataContent) => {
		const langItem = item.dataLanguages?.length
			? item.dataLanguages.find((lang) => fixed.includes(lang.id))
			: fixed.includes(item.id)
				? item
				: null;

		if (langItem) {
			const newSelIds = fixed.filter((id) => id !== langItem.id);
			const selItems = selectedItems.filter((item) => item.id !== langItem.id);
			setState((state) => ({ ...state, fixed: newSelIds, selectedItems: selItems }));
			handleListDelete(newSelIds);
		}
	};

	const handleDragStart = (event: DragStartEvent) => {
		const { active } = event;
		setActiveId(active.id as number);
	};

	const handleOnDragEnd = (event: DragEndEvent) => {
		const { active, over } = event;

		if (over && active.id !== over.id) {
			const item = selectedItems.find((elem) => elem.id === active.id);
			const newIndex = selectedItems.map((e) => e.id).indexOf(over.id as number);

			if (!item) return;

			const newFixed = setReorderElements(item, newIndex);
			handleListMove(newFixed);
		}

		setActiveId(null);
	};

	return (
		<S.ItemList>
			{isLoading && (
				<S.LoadingWrapper>
					<Loader name="circle" size="40" />
				</S.LoadingWrapper>
			)}
			{!isLoading && selectedItems.length > 0 && (
				<DndContext
					collisionDetection={closestCenter}
					onDragEnd={handleOnDragEnd}
					onDragStart={handleDragStart}
					sensors={sensors}
				>
					<SortableContext items={itemsIds} strategy={verticalListSortingStrategy}>
						{selectedItems.map((item) => {
							const source = sourceTitles.find((el) => el.id === item.structuredData);

							// Warn if source is missing but still render the item
							if (!source) {
								console.warn(`Source not found for item ${item.id} (structuredData: ${item.structuredData})`);
							}

							return (
								<Item
									key={item.id}
									item={item}
									handleDelete={handleDelete}
									listLength={selectedItems.length}
									source={source}
									isDragging={item.id === activeId}
								/>
							);
						})}
					</SortableContext>
				</DndContext>
			)}
		</S.ItemList>
	);
};

interface IProps {
	items: number[];
	currentSite: number | null;
	handleListDelete(value: number[]): void;
	handleListMove(value: number[]): void;
	site?: SiteValue | null;
}

const mapStateToProps = (state: IRootState) => ({
	currentSite: state.sites.currentSiteInfo?.id || null,
});

export default connect(mapStateToProps)(ItemList);
