import { useCallback, useEffect, useState } from "react";
import { useSubscribe } from "react-eventizer";

import { useDialogStore } from "@/contexts/dialog/lib/useDialogStore";
import {
	getAlarmScopeById,
	getAlarmScopeList,
	useGetAlarmScopeList,
} from "@/contracts/alarm-scopes";
import {
	AlarmModel,
	AlarmScopeModel,
	AlarmScopeStatus,
	AlarmScopeStatusChangingModel,
} from "@/contracts/client.schemas";
import { useIdentityStore } from "@/features";
import {
	getLastAlarmScopeStatusChanging,
	getStepByStatus,
} from "@/lib/alarm-scope.helper";
import { useWatcherStore } from "@/modules/watcher/store/watcher.store";
import { Steps } from "@/pages/AlarmDialog/model/Steps";
import { ALARM_DIALOG_KEY } from "@/pages/AlarmDialog/ui/AlarmDialog";
import { AlarmDialogProvider } from "@/pages/AlarmDialog/ui/AlarmDialogProvider";
import { useAlarmStore } from "@/store/alarm.store";

const CommonOperatorStatuses: AlarmScopeStatus[] = [AlarmScopeStatus.New];
const OnlyThisOperatorStatuses: AlarmScopeStatus[] = [
	AlarmScopeStatus.Accepted,
	AlarmScopeStatus.CamerasViewed,
	AlarmScopeStatus.CallsCompleted,
	AlarmScopeStatus.CommentAndSend,
];

/**
 * Сервис для работы с тревогами.
 * Singleton.
 */
export function useAlarmService() {
	const { data: alarmScopeList, refetch: refetchAlarmScopeList } =
		useGetAlarmScopeList({
			query: {
				initialData: [],
			},
		});
	const { push, getIsOpen } = useDialogStore();
	const { user } = useIdentityStore();
	const setCountOfAlarmScopes = useAlarmStore((x) => x.setCountOfAlarmScopes);
	const [scopes, setScopes] = useState<AlarmScopeModel[]>([]);
	const setCommonAlarmScopes = useWatcherStore((x) => x.setCommonAlarmScopes);

	const isAlarmDialogOpen = useCallback(() => {
		return getIsOpen(ALARM_DIALOG_KEY);
	}, [getIsOpen]);

	const handleOnNext = useCallback(() => {
		refetchAlarmScopeList();
	}, [refetchAlarmScopeList]);

	const openAlarmDialog = useCallback(
		(scope: AlarmScopeModel, step: Steps) => {
			push({
				key: ALARM_DIALOG_KEY,
				content: (
					<AlarmDialogProvider
						initialScope={scope}
						initialStep={step}
						onNext={handleOnNext}
					/>
				),
			});
		},
		[push, handleOnNext],
	);

	const tryOpenScope = useCallback(
		async (scope: AlarmScopeModel) => {
			if (isAlarmDialogOpen()) {
				return;
			}

			const scopeStatus = getLastAlarmScopeStatusChanging(scope);

			const allow = isAllowToProcessScope(scopeStatus, user?.id || "");
			if (!allow) {
				return;
			}

			openAlarmDialog(scope, getStepByStatus(scopeStatus.status));
		},
		[isAlarmDialogOpen, openAlarmDialog, user?.id],
	);

	const tryOpenFirstScope = useCallback(
		async (scopeList: AlarmScopeModel[]) => {
			const scope = scopeList.find((item) => {
				const scopeStatus = getLastAlarmScopeStatusChanging(item);
				return isAllowToProcessScope(scopeStatus, user?.id || "");
			});

			if (!scope) {
				return;
			}

			await tryOpenScope(scope);
		},
		[tryOpenScope, user?.id],
	);

	const init = useCallback(
		async (items: AlarmScopeModel[]) => {
			setCountOfAlarmScopes(items.length);

			if (items.length === 0) {
				return;
			}

			await tryOpenFirstScope(items);
		},
		[setCountOfAlarmScopes, tryOpenFirstScope],
	);

	useEffect(() => {
		init(alarmScopeList);
		setScopes(alarmScopeList);
		setCommonAlarmScopes(alarmScopeList);
	}, [alarmScopeList, init]);

	useSubscribe("ALARMS.CREATED", handleNewAlarmCreated);
	async function handleNewAlarmCreated(alarm: AlarmModel) {
		try {
			const scope = await getOrLoadScope(alarm);
			await tryOpenScope(scope);
		} catch (error) {
			console.error("Error handling new alarm:", error);
		}
	}

	useSubscribe("OBJECTWATCHER.CLICK", handleObjectWatcherClick);
	async function handleObjectWatcherClick() {
		const scopes = await getAlarmScopeList();
		await tryOpenFirstScope(scopes);
	}

	useSubscribe("ALARMASSET.CLICK", handleAlarmAssetClick);
	async function handleAlarmAssetClick({ assetId }: { assetId: string }) {
		const scopes = await getAlarmScopeList();
		const scope = scopes.find((x) => x.assetId === assetId);
		if (!scope) {
			return;
		}
		await tryOpenScope(scope);
	}

	async function getOrLoadScope(alarm: AlarmModel) {
		let scope = getScopeById(alarm.scopeId);
		if (!scope) {
			scope = await getAlarmScopeById(alarm.scopeId);
			setScopes((prev) => [...prev, scope!]);
		}
		return scope;
	}

	function getScopeById(id: string) {
		return scopes.find((x) => x.id === id);
	}

	function isAllowToProcessScope(
		scopeStatusChanging: AlarmScopeStatusChangingModel,
		userId: string,
	) {
		if (CommonOperatorStatuses.includes(scopeStatusChanging.status)) {
			return true;
		}
		if (OnlyThisOperatorStatuses.includes(scopeStatusChanging.status)) {
			return scopeStatusChanging.userId === userId;
		}
		return false;
	}
}
