import { FC, useEffect } from "react";
import { useEmitter } from "react-eventizer";
import { useTranslation } from "react-i18next";
import { toast } from "react-toastify";

import { useSignalR } from "@/contexts/signalR/useSignalR";
import { getAreaList } from "@/contracts/areas";
import { useGetAssetList } from "@/contracts/assets";
import { useGetAssetGroupList } from "@/contracts/assets-groups";
import {
	AlarmModel,
	AssetGroupModel,
	AssetModel,
	EventModel,
	FaultModel,
	UserLogModel,
} from "@/contracts/client.schemas";
import { getEventList } from "@/contracts/events";
import { useGetFaultList } from "@/contracts/faults";
import { getLogList } from "@/contracts/logs";
import { getZoneList } from "@/contracts/zones";
import { useInterval } from "@/hooks/use-interval";

import { maxPingDelaySeconds } from "../../constants";
import {
	subscribeToNotifications,
	unsubscribeFromNotifications,
} from "../../utils/subscribe-all";

import { usePingStore } from "./store/ping.store";
import { useWatcherStore } from "./store/watcher.store";

const ASSETS = "ASSETS";
const ASSET_GROUPS = "ASSET_GROUPS";

export const WatcherSubscriptions: FC = () => {
	const { t } = useTranslation();
	const { subscribe, unsubscribe } = useSignalR();
	const watcherStore = useWatcherStore();
	const pingStore = usePingStore();
	const { data: assets } = useGetAssetList();
	const { data: groups } = useGetAssetGroupList();
	const { data: faults } = useGetFaultList();
	const emitAssetCreated = useEmitter("ASSETS.CREATED");
	const emitAssetDeleted = useEmitter("ASSETS.DELETED");
	const emitAssetUpdated = useEmitter("ASSETS.UPDATED");
	const emitAssetGroupCreated = useEmitter("ASSET_GROUPS.CREATED");
	const emitAssetGroupDeleted = useEmitter("ASSET_GROUPS.DELETED");
	const emitAssetGroupUpdated = useEmitter("ASSET_GROUPS.UPDATED");
	const emitAlarmCreated = useEmitter("ALARMS.CREATED");
	const emitFaultCreated = useEmitter("FAULTS.CREATED");
	const emitFaultDeleted = useEmitter("FAULTS.DELETED");
	const emitEventCreated = useEmitter("EVENTS.CREATED");

	useEffect(() => {
		const subscribeToAll = () => {
			subscribeToNotifications(
				subscribe,
				ASSETS,
				tryAddAsset,
				tryRemoveAsset,
				tryUpdateAsset,
			);
			subscribeToNotifications(
				subscribe,
				ASSET_GROUPS,
				tryAddGroup,
				tryRemoveGroup,
				tryUpdateGroup,
			);

			subscribe("ALARMS.CREATED", handleAlarmCreated);
			subscribe("ALARMS.ACCEPTED", handleAlarmAccepted);
			subscribe("ALARMS.RESOLVED", handleAlarmResolved);
			subscribe("FAULTS.CREATED", handleFaultCreated);
			subscribe("FAULTS.DELETED", handleFaultDeleted);
			subscribe("EVENTS.CREATED", handleEventCreated);
			subscribe("LOGS.CREATED", handleLogCreated);

			subscribe("PING", onPing);
		};

		const unsubscribeFromAll = () => {
			unsubscribe("PING", onPing);

			unsubscribe("LOGS.CREATED", handleLogCreated);
			unsubscribe("EVENTS.CREATED", handleEventCreated);
			unsubscribe("FAULTS.DELETED", handleFaultDeleted);
			unsubscribe("FAULTS.CREATED", handleFaultCreated);
			unsubscribe("ALARMS.RESOLVED", handleAlarmResolved);
			unsubscribe("ALARMS.ACCEPTED", handleAlarmAccepted);
			unsubscribe("ALARMS.CREATED", handleAlarmCreated);

			unsubscribeFromNotifications(
				unsubscribe,
				ASSET_GROUPS,
				tryAddGroup,
				tryRemoveGroup,
				tryUpdateGroup,
			);
			unsubscribeFromNotifications(
				unsubscribe,
				ASSETS,
				tryAddAsset,
				tryRemoveAsset,
				tryUpdateAsset,
			);
		};

		const tryAddAsset = (model: AssetModel) => {
			watcherStore.addAsset(model);
			emitAssetCreated(model);
		};

		const tryRemoveAsset = (model: AssetModel) => {
			watcherStore.removeAsset(model.id);
			emitAssetDeleted(model);
		};

		const tryUpdateAsset = (model: AssetModel) => {
			watcherStore.updateAsset(model);
			emitAssetUpdated(model);
		};

		const tryAddGroup = (model: AssetGroupModel) => {
			watcherStore.addGroup(model);
			emitAssetGroupCreated(model);
		};

		const tryRemoveGroup = (model: AssetGroupModel) => {
			watcherStore.removeGroup(model.id);
			emitAssetGroupDeleted(model);
		};

		const tryUpdateGroup = (model: AssetGroupModel) => {
			watcherStore.updateGroup(model);
			emitAssetGroupUpdated(model);
		};

		const handleAlarmCreated = (model: AlarmModel) => {
			watcherStore.addCommonAlarm(model);

			emitAlarmCreated(model);
		};
		const handleAlarmResolved = (model: AlarmModel) => {
			watcherStore.removeCommonAlarm(model.id);
		};
		const handleAlarmAccepted = (model: AlarmModel) => {
			watcherStore.removeCommonAlarm(model.id);
		};

		const handleFaultCreated = (model: FaultModel) => {
			watcherStore.addCommonFault(model);
			emitFaultCreated(model);
		};
		const handleFaultDeleted = (model: FaultModel) => {
			watcherStore.removeCommonFault(model.id);
			emitFaultDeleted(model);
		};

		const handleEventCreated = (event: EventModel) => {
			watcherStore.tryAddAssetEvent(event);
			emitEventCreated(event);

			watcherStore.eventHandlers.forEach((handler) => handler(event));
		};

		const handleLogCreated = (log: UserLogModel) => {
			watcherStore.addAssetLog(log);
		};

		const onPing = () => {
			pingStore.setPingTimer(expiredTime());
		};

		subscribeToAll();

		return () => {
			unsubscribeFromAll();
		};

		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);

	useEffect(() => {
		watcherStore.setAssets(assets?.items ?? []);
	}, [assets]);

	useEffect(() => {
		watcherStore.setGroups(groups?.items ?? []);
	}, [groups]);

	useEffect(() => {
		watcherStore.setCommonFaults(faults?.items ?? []);
	}, [faults]);

	// Current asset changed
	useEffect(() => {
		const currentAssetId = watcherStore.currentAssetId;
		if (!currentAssetId) {
			watcherStore.setAssetEvents([]);
			watcherStore.setAssetLogs([]);

			return;
		}

		// TODO: move events to asset card
		// if (!dialog.isOpened(Dialogs.EVENT_CARD)) {
		// 	dialog.open(Dialogs.EVENT_CARD, { assetId: currentAssetId });
		// }

		const load = async () => {
			try {
				watcherStore.setLoadingEventsAndLogs(true);

				const [events, logs, areas, zones] = await Promise.all([
					getEventList({ AssetId: currentAssetId }),
					getLogList({ AssetId: currentAssetId }),
					getAreaList({ AssetId: currentAssetId }),
					getZoneList({ AssetId: currentAssetId }),
				]);
				watcherStore.setAssetEvents(events.items);
				watcherStore.setAssetLogs(logs.items);
				watcherStore.setAssetAreas(areas.items);
				watcherStore.setAssetZones(zones.items);
			} catch (error) {
				console.error("Error loading logs and events:", error);
				toast.error(t("Error loading logs and events"));
			} finally {
				watcherStore.setLoadingEventsAndLogs(false);
			}
		};

		load();

		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [watcherStore.currentAssetId]);

	// Check connection time
	useInterval(() => {
		const nowMilliseconds = Date.now();
		const pingMilliseconds = pingStore.pingTimer.getTime();
		pingStore.setIsServerConnected(nowMilliseconds <= pingMilliseconds);
	}, 10000);

	return null;
};

const expiredTime = () => {
	return new Date(Date.now() + maxPingDelaySeconds * 1000);
};
