import { create } from "zustand";

import {
	AssetGroupModel,
	AssetModel,
	AlarmModel,
	AreaModel,
	FaultModel,
	EventModel,
	UserLogModel,
	AlarmScopeModel,
	ZoneModel,
} from "@/contracts/client.schemas";

interface State {
	assets: AssetModel[];
	setAssets: (assets: AssetModel[]) => void;
	addAsset: (asset: AssetModel) => void;
	removeAsset: (assetId: string) => void;
	updateAsset: (asset: AssetModel) => void;

	groups: AssetGroupModel[];
	setGroups: (groups: AssetGroupModel[]) => void;
	addGroup: (group: AssetGroupModel) => void;
	removeGroup: (groupId: string) => void;
	updateGroup: (group: AssetGroupModel) => void;

	commonAlarmScopes: AlarmScopeModel[];
	setCommonAlarmScopes: (alarmScopes: AlarmScopeModel[]) => void;
	addCommonAlarmScope: (alarmScope: AlarmScopeModel) => void;
	removeCommonAlarmScope: (alarmScopeId: string) => void;

	commonAlarms: AlarmModel[];
	setCommonAlarms: (alarms: AlarmModel[]) => void;
	addCommonAlarm: (alarm: AlarmModel) => void;
	removeCommonAlarm: (alarmId: string) => void;

	commonFaults: FaultModel[];
	setCommonFaults: (faults: FaultModel[]) => void;
	addCommonFault: (fault: FaultModel) => void;
	removeCommonFault: (faultId: string) => void;

	assetAlarms: () => AlarmModel[];
	assetFaults: () => FaultModel[];

	assetEvents: EventModel[];
	setAssetEvents: (events: EventModel[]) => void;
	tryAddAssetEvent: (event: EventModel) => void;
	assetLogs: UserLogModel[];
	setAssetLogs: (logs: UserLogModel[]) => void;
	addAssetLog: (log: UserLogModel) => void;
	assetAreas: AreaModel[];
	setAssetAreas: (areas: AreaModel[]) => void;
	addAssetArea: (area: AreaModel) => void;
	assetZones: ZoneModel[];
	setAssetZones: (zones: ZoneModel[]) => void;
	addAssetZone: (zone: ZoneModel) => void;

	currentAssetId?: string;
	setCurrentAssetId: (assetId?: string) => void;
	loadingAlarmsAndFaults: boolean;
	setLoadingAlarmsAndFaults: (loading: boolean) => void;
	loadingEventsAndLogs: boolean;
	setLoadingEventsAndLogs: (loading: boolean) => void;
	loadingAssetAndGroups: boolean;
	setLoadingAssetAndGroups: (loading: boolean) => void;
	eventHandlers: Set<(event: EventModel) => void>;
	addEventHandler: (callback: (event: EventModel) => void) => void;
	removeEventHandler: (callback: (event: EventModel) => void) => void;

	clearAll: () => void;
}

export const useWatcherStore = create<State>((set, get) => ({
	assets: [],
	setAssets: (assets) => set({ assets }),
	addAsset: (asset) =>
		set((state) => {
			if (!state.assets.find((a) => a.id === asset.id)) {
				return { assets: [...state.assets, asset] };
			}
			return state;
		}),
	removeAsset: (assetId) =>
		set((state) => ({
			assets: state.assets.filter((asset) => asset.id !== assetId),
		})),
	updateAsset: (asset) =>
		set((state) => ({
			assets: state.assets.map((a) => (a.id === asset.id ? asset : a)),
		})),

	groups: [],
	setGroups: (groups) => set({ groups }),
	addGroup: (group) =>
		set((state) => {
			if (!state.groups.find((g) => g.id === group.id)) {
				return { groups: [...state.groups, group] };
			}
			return state;
		}),
	removeGroup: (groupId) =>
		set((state) => ({
			groups: state.groups.filter((group) => group.id !== groupId),
		})),
	updateGroup: (group) =>
		set((state) => ({
			groups: state.groups.map((g) => (g.id === group.id ? group : g)),
		})),

	commonAlarmScopes: [],
	setCommonAlarmScopes: (alarmScopes) =>
		set({ commonAlarmScopes: alarmScopes }),
	addCommonAlarmScope: (alarmScope) =>
		set((state) => {
			if (!state.commonAlarmScopes.find((a) => a.id === alarmScope.id)) {
				return {
					commonAlarmScopes: [...state.commonAlarmScopes, alarmScope],
				};
			}
			return state;
		}),
	removeCommonAlarmScope: (alarmScopeId) =>
		set((state) => ({
			commonAlarmScopes: state.commonAlarmScopes.filter(
				(alarmScope) => alarmScope.id !== alarmScopeId,
			),
		})),

	commonAlarms: [],
	setCommonAlarms: (alarms) => set({ commonAlarms: alarms }),
	addCommonAlarm: (alarm) =>
		set((state) => {
			// TODO: Optimize this with Set
			if (state.commonAlarms.find((a) => a.id === alarm.id)) {
				return state;
			}

			return { commonAlarms: [alarm, ...state.commonAlarms] };
		}),
	removeCommonAlarm: (alarmId) =>
		set((state) => ({
			commonAlarms: state.commonAlarms.filter(
				(alarm) => alarm.id !== alarmId,
			),
		})),

	commonFaults: [],
	setCommonFaults: (faults) => set({ commonFaults: faults }),
	addCommonFault: (fault) =>
		set((state) => {
			// TODO: Optimize this with Set
			if (state.commonFaults.find((a) => a.id === fault.id)) {
				return state;
			}

			return { commonFaults: [fault, ...state.commonFaults] };
		}),
	removeCommonFault: (faultId) =>
		set((state) => ({
			commonFaults: state.commonFaults.filter(
				(fault) => fault.id !== faultId,
			),
		})),

	assetAlarms: () =>
		get().commonAlarms.filter(
			(alarm) => alarm.event.assetId === get().currentAssetId,
		),
	assetFaults: () =>
		get().commonFaults.filter(
			(fault) => fault.event.assetId === get().currentAssetId,
		),

	assetEvents: [],
	setAssetEvents: (events) => set({ assetEvents: events }),
	tryAddAssetEvent: (event) =>
		set((state) => {
			// TODO: Optimize this with Set
			if (
				event.assetId !== state.currentAssetId ||
				state.assetEvents.find((a) => a.id === event.id)
			) {
				return state;
			}

			return { assetEvents: [event, ...state.assetEvents] };
		}),

	assetLogs: [],
	setAssetLogs: (logs) => set({ assetLogs: logs }),
	addAssetLog: (log) =>
		set((state) => {
			// TODO: Optimize this with Set
			if (
				log.assetId !== state.currentAssetId ||
				state.assetLogs.find((a) => a.id === log.id)
			) {
				return state;
			}

			return { assetLogs: [log, ...state.assetLogs] };
		}),

	assetAreas: [],
	setAssetAreas: (areas) => set({ assetAreas: areas }),
	addAssetArea: (area) =>
		set((state) => {
			// TODO: Optimize this with Set
			if (
				area.assetId !== state.currentAssetId ||
				state.assetAreas.find((a) => a.id === area.id)
			) {
				return state;
			}

			return { assetAreas: [area, ...state.assetAreas] };
		}),

	assetZones: [],
	setAssetZones: (zones) => set({ assetZones: zones }),
	addAssetZone: (zone) =>
		set((state) => {
			// TODO: Optimize this with Set
			if (
				!state.assetAreas.some((area) => area.id === zone.areaId) ||
				state.assetZones.find((a) => a.id === zone.id)
			) {
				return state;
			}

			return { assetZones: [zone, ...state.assetZones] };
		}),

	currentAssetId: undefined,
	setCurrentAssetId: (assetId) => set({ currentAssetId: assetId }),

	loadingAlarmsAndFaults: false,
	setLoadingAlarmsAndFaults: (loading) =>
		set({ loadingAlarmsAndFaults: loading }),

	loadingEventsAndLogs: false,
	setLoadingEventsAndLogs: (loading) =>
		set({ loadingEventsAndLogs: loading }),

	loadingAssetAndGroups: false,
	setLoadingAssetAndGroups: (loading) =>
		set({ loadingAssetAndGroups: loading }),

	eventHandlers: new Set(),
	addEventHandler: (callback) =>
		set((state) => {
			state.eventHandlers.add(callback);
			return {
				eventHandlers: new Set(state.eventHandlers),
			};
		}),
	removeEventHandler: (callback) =>
		set((state) => {
			state.eventHandlers.delete(callback);
			return {
				eventHandlers: new Set(state.eventHandlers),
			};
		}),

	clearAll: () => {
		set({
			assets: [],
			groups: [],
			commonAlarms: [],
			commonFaults: [],
			assetEvents: [],
			assetLogs: [],
			assetAreas: [],
			assetZones: [],
			currentAssetId: undefined,
			loadingAlarmsAndFaults: false,
			loadingEventsAndLogs: false,
			loadingAssetAndGroups: false,
			eventHandlers: new Set(),
		});
	},
}));
