import { PropsWithChildren, useState } from "react";

import { AssetGroupModel, AssetModel } from "@/contracts/client.schemas";
import { useWatcherStore } from "@/modules/watcher/store/watcher.store";

import { context } from "./context";

export interface Context {
	activeAssetId?: string;
	setActiveAssetId: (id?: string) => void;

	isActiveGroup: (groupId: string) => boolean;
	activateGroup: (groupId: string) => void;
	deactivateGroup: (groupId: string) => void;

	groups: AssetGroupModel[];
	assets: AssetModel[];

	selectedAssetIds: Set<string>;
	isSelectedGroup: (groupId: string) => boolean | "indeterminate";
	isSelectedAsset: (assetId: string) => boolean;

	selectGroup: (id: string) => void;
	unselectGroup: (id: string) => void;
	selectAsset: (id: string) => void;
	unselectAsset: (id: string) => void;
}

export function ReportAssetListContextProvider(props: PropsWithChildren) {
	const [activeAssetId, setActiveAssetId] = useState<string>();
	const [activeGroupIdList, setActiveGroupIdList] = useState(
		new Set<string>(),
	);
	const [selectedAssetIds, setSelectedAssetIds] = useState(new Set<string>());

	const groups = useWatcherStore((x) => x.groups).sort((a, b) =>
		a.name.localeCompare(b.name),
	);
	const assets = useWatcherStore((x) => x.assets).sort((a, b) =>
		a.name.localeCompare(b.name),
	);

	function isActiveGroup(groupId: string) {
		return activeGroupIdList.has(groupId);
	}

	function activateGroup(groupId: string) {
		const newSet = new Set<string>();
		let id: string | undefined | null = groupId;
		while (id) {
			newSet.add(id);
			id = groups.find((x) => x.id === id)?.parentId;
		}

		// TODO: странно
		for (const groupId of activeGroupIdList) {
			activeGroupIdList.add(groupId);
		}

		setActiveGroupIdList(newSet);
	}

	function deactivateGroup(groupId: string) {
		activeGroupIdList.delete(groupId);
	}

	function isSelectedGroup(groupId: string): boolean | "indeterminate" {
		const assetIds = collectAssetInGroup(groupId);
		const allSelected = assetIds.every((x) => selectedAssetIds.has(x));
		const noneSelected = assetIds.every((x) => !selectedAssetIds.has(x));

		if (allSelected) return true;
		if (noneSelected) return false;
		return "indeterminate";
	}

	function isSelectedAsset(assetId: string) {
		return selectedAssetIds.has(assetId);
	}

	function selectGroup(id: string) {
		const group = groups.find((x) => x.id === id);
		if (!group) {
			throw new Error("Group not found");
		}

		const assetIds = collectAssetInGroup(id);
		for (const assetId of assetIds) {
			selectAsset(assetId);
		}
	}

	function unselectGroup(id: string) {
		const group = groups.find((x) => x.id === id);
		if (!group) {
			throw new Error("Group not found");
		}

		const assetIds = collectAssetInGroup(id);
		for (const assetId of assetIds) {
			unselectAsset(assetId);
		}
	}

	function collectAssetInGroup(groupId: string): string[] {
		const group = groups.find((x) => x.id === groupId);
		if (!group) {
			return [];
		}

		const assetIds: string[] = [];

		assetIds.push(
			...assets
				.filter((x) => x.groupIds.includes(groupId))
				.map((x) => x.id),
		);

		for (const childGroupId of groups.filter(
			(x) => x.parentId === groupId,
		)) {
			assetIds.push(...collectAssetInGroup(childGroupId.id));
		}

		return assetIds;
	}

	function selectAsset(id: string) {
		if (selectedAssetIds.has(id)) {
			return;
		}

		setSelectedAssetIds((prev) => {
			const newSet = new Set(prev);
			newSet.add(id);
			return newSet;
		});
	}

	function unselectAsset(id: string) {
		if (!selectedAssetIds.has(id)) {
			return;
		}

		setSelectedAssetIds((prev) => {
			const newSet = new Set(prev);
			newSet.delete(id);
			return newSet;
		});
	}

	return (
		<context.Provider
			value={{
				activeAssetId,
				setActiveAssetId,
				isActiveGroup,
				activateGroup,
				deactivateGroup,
				groups,
				assets,

				selectedAssetIds,
				isSelectedGroup,
				isSelectedAsset,

				selectAsset,
				unselectAsset,
				selectGroup,
				unselectGroup,
			}}
		>
			{props.children}
		</context.Provider>
	);
}
