import { AdminServicesStore, UpdateServiceStore, DeleteServiceStore } from '$houdini';
import { fromGlobalId, toGlobalId } from '$lib/utils/relay';
import { formatAddress, type AccountAddressInfo } from '$lib/utils/lookup';
import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity';

// Enriched service type
export type EnrichedService = {
	id: string;
	date: string;
	status: string;
	notes: string | null;
	teamMembers: { pk: string }[];
	accountAddressId: string;
	accountName: string;
	addressName: string | null;
	address: string | null;
};

export type ViewMode = 'date' | 'week' | 'account';
export type TabType = 'unassigned' | 'readyToAssign' | 'assigned';
export type ColumnType = TabType;

export interface TeamProfile {
	id: string;
	fullName: string;
	role: string;
}

export function createAssignServicesStore() {
	// Services stores (mutation stores don't need state)
	const servicesStore = new AdminServicesStore();
	const updateServiceStore = new UpdateServiceStore();
	const deleteServiceStore = new DeleteServiceStore();

	// Store fetched services data
	let servicesData = $state<
		{
			id: string;
			date: string;
			status: string;
			notes: string | null;
			accountAddressId: string;
			teamMembers: { pk: string }[] | null;
		}[]
	>([]);

	// External data (set via init)
	const accountLookup = new SvelteMap<string, AccountAddressInfo>();
	let teamProfiles = $state<TeamProfile[]>([]);

	// Computed from team profiles
	const dispatchProfile = $derived(teamProfiles.find((p) => p.role === 'ADMIN') ?? null);
	const dispatchProfilePk = $derived(dispatchProfile ? fromGlobalId(dispatchProfile.id) : null);
	const dispatchProfileGlobalId = $derived(dispatchProfile?.id ?? null);
	const nonAdminTeamMembers = $derived(teamProfiles.filter((p) => p.role !== 'ADMIN'));

	// UI State
	let currentMonth = new SvelteDate();
	let isLoading = $state(true);
	let error = $state('');
	let viewMode = $state<ViewMode>('date');
	let activeTab = $state<TabType>('unassigned');

	// Track services currently being updated
	const updatingServices = new SvelteSet<string>();

	// Track which service is being date-edited
	let editingDateServiceId = $state<string | null>(null);

	// Optimistic updates for team members
	const optimisticUpdates = new SvelteMap<string, { pk: string }[]>();

	// Staged team members (not yet submitted)
	const stagedTeamMembers = new SvelteMap<string, string[]>();

	// Track which service has dropdown open
	let openTeamMemberDropdown = $state<string | null>(null);

	// Track expanded groups per column
	const expandedUnassigned = new SvelteSet<string>();
	const expandedReadyToAssign = new SvelteSet<string>();
	const expandedAssigned = new SvelteSet<string>();

	// Bulk selection state per column
	const selectedUnassigned = new SvelteSet<string>();
	const selectedReadyToAssign = new SvelteSet<string>();
	const selectedAssigned = new SvelteSet<string>();

	// Bulk operation state
	let isBulkOperating = $state(false);
	let bulkSelectedTeamMember = $state<string | null>(null);
	let showBulkTeamMemberDropdown = $state(false);

	// Delete modal state
	let showDeleteModal = $state(false);
	let serviceToDelete = $state<{
		id: string;
		accountName: string;
		date: string;
	} | null>(null);
	let isDeleting = $state(false);

	// Initialize with external data
	function init(lookup: Map<string, AccountAddressInfo>, profiles: TeamProfile[]) {
		accountLookup.clear();
		for (const [key, value] of lookup) {
			accountLookup.set(key, value);
		}
		teamProfiles = profiles;
	}

	// Fetch services for current month
	async function fetchServices() {
		isLoading = true;
		error = '';

		const year = currentMonth.getFullYear();
		const month = currentMonth.getMonth() + 1;
		const startDate = `${year}-${String(month).padStart(2, '0')}-01`;
		const lastDay = new SvelteDate(year, month, 0).getDate();
		const endDate = `${year}-${String(month).padStart(2, '0')}-${String(lastDay).padStart(2, '0')}`;

		try {
			const result = await servicesStore.fetch({
				variables: {
					filters: {
						date: { gte: startDate, lte: endDate },
						status: { exact: 'SCHEDULED' }
					}
				}
			});
			servicesData = result.data?.services ?? [];
		} catch (e) {
			error = e instanceof Error ? e.message : 'Failed to load services';
		} finally {
			isLoading = false;
		}
	}

	// Enrich services with account info
	const enrichedServices = $derived.by(() => {
		const result: EnrichedService[] = [];

		for (const service of servicesData) {
			const accountInfo = service.accountAddressId
				? accountLookup.get(service.accountAddressId)
				: null;

			if (!accountInfo) continue;

			const teamMembers = optimisticUpdates.has(service.id)
				? optimisticUpdates.get(service.id)!
				: (service.teamMembers ?? []);

			result.push({
				id: service.id,
				date: service.date,
				status: service.status,
				notes: service.notes,
				teamMembers,
				accountAddressId: service.accountAddressId,
				accountName: accountInfo.accountName,
				addressName: accountInfo.addressName ?? null,
				address: formatAddress(accountInfo)
			});
		}

		return result.sort((a, b) => a.date.localeCompare(b.date));
	});

	// Categorize services
	const categorizedServices = $derived.by(() => {
		const unassigned: EnrichedService[] = [];
		const readyToAssign: EnrichedService[] = [];
		const assigned: EnrichedService[] = [];

		for (const service of enrichedServices) {
			const hasTeam = service.teamMembers.length > 0;
			const hasDispatch = dispatchProfilePk
				? service.teamMembers.some((m) => m.pk === dispatchProfilePk)
				: false;
			const hasOthers = service.teamMembers.length > (hasDispatch ? 1 : 0);

			if (!hasTeam) {
				unassigned.push(service);
			} else if (hasDispatch && hasOthers) {
				assigned.push(service);
			} else if (hasDispatch) {
				readyToAssign.push(service);
			} else {
				unassigned.push(service);
			}
		}

		return { unassigned, readyToAssign, assigned };
	});

	// Grouping functions
	function groupByDate(services: EnrichedService[]): Map<string, EnrichedService[]> {
		const groups = new SvelteMap<string, EnrichedService[]>();
		for (const service of services) {
			const date = new SvelteDate(service.date + 'T00:00:00');
			const key = date.toLocaleDateString('en-US', {
				weekday: 'short',
				month: 'short',
				day: 'numeric'
			});
			const existing = groups.get(key) ?? [];
			existing.push(service);
			groups.set(key, existing);
		}
		return groups;
	}

	function groupByWeek(services: EnrichedService[]): Map<string, EnrichedService[]> {
		const groups = new SvelteMap<string, EnrichedService[]>();
		for (const service of services) {
			const date = new SvelteDate(service.date + 'T00:00:00');
			const dayOfWeek = date.getDay();
			const weekStart = new SvelteDate(date);
			weekStart.setDate(date.getDate() - dayOfWeek);
			const weekEnd = new SvelteDate(weekStart);
			weekEnd.setDate(weekStart.getDate() + 6);

			const key = `${weekStart.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} - ${weekEnd.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}`;
			const existing = groups.get(key) ?? [];
			existing.push(service);
			groups.set(key, existing);
		}
		return groups;
	}

	function groupByAccount(services: EnrichedService[]): Map<string, EnrichedService[]> {
		const groups = new SvelteMap<string, EnrichedService[]>();
		for (const service of services) {
			const key = service.accountName;
			const existing = groups.get(key) ?? [];
			existing.push(service);
			groups.set(key, existing);
		}
		return new SvelteMap([...groups.entries()].sort((a, b) => a[0].localeCompare(b[0])));
	}

	function getGroupedServices(services: EnrichedService[]): Map<string, EnrichedService[]> {
		switch (viewMode) {
			case 'date':
				return groupByDate(services);
			case 'week':
				return groupByWeek(services);
			case 'account':
				return groupByAccount(services);
		}
	}

	// Grouped data
	const groupedUnassigned = $derived(getGroupedServices(categorizedServices.unassigned));
	const groupedReadyToAssign = $derived(getGroupedServices(categorizedServices.readyToAssign));
	const groupedAssigned = $derived(getGroupedServices(categorizedServices.assigned));

	// Month navigation
	function previousMonth() {
		currentMonth = new SvelteDate(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1);
	}

	function nextMonth() {
		currentMonth = new SvelteDate(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1);
	}

	function goToCurrentMonth() {
		currentMonth = new SvelteDate();
	}

	const monthDisplay = $derived(
		currentMonth.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })
	);

	// Tab navigation
	const tabOrder: TabType[] = ['unassigned', 'readyToAssign', 'assigned'];

	function previousTab() {
		const currentIndex = tabOrder.indexOf(activeTab);
		activeTab = tabOrder[(currentIndex - 1 + tabOrder.length) % tabOrder.length];
	}

	function nextTab() {
		const currentIndex = tabOrder.indexOf(activeTab);
		activeTab = tabOrder[(currentIndex + 1) % tabOrder.length];
	}

	// Toggle group expansion
	function toggleGroup(column: ColumnType, groupKey: string) {
		const set =
			column === 'unassigned'
				? expandedUnassigned
				: column === 'readyToAssign'
					? expandedReadyToAssign
					: expandedAssigned;
		if (set.has(groupKey)) {
			set.delete(groupKey);
		} else {
			set.add(groupKey);
		}
	}

	// Reset expanded groups when view mode changes
	function resetExpandedGroups() {
		expandedUnassigned.clear();
		expandedReadyToAssign.clear();
		expandedAssigned.clear();
	}

	// Get team member names
	function getTeamMemberNames(teamMembers: { pk: string }[]): string[] {
		return teamMembers
			.map((m) => {
				const profile = teamProfiles.find((p) => fromGlobalId(p.id) === m.pk);
				return profile?.fullName ?? null;
			})
			.filter((name): name is string => name !== null);
	}

	function getNonDispatchTeamMemberNames(teamMembers: { pk: string }[]): string[] {
		return teamMembers
			.filter((m) => m.pk !== dispatchProfilePk)
			.map((m) => {
				const profile = teamProfiles.find((p) => fromGlobalId(p.id) === m.pk);
				return profile?.fullName ?? null;
			})
			.filter((name): name is string => name !== null);
	}

	// Service operations
	// Helper to update servicesData after successful mutation
	function updateServiceTeamMembers(serviceId: string, newTeamMembers: { pk: string }[]) {
		servicesData = servicesData.map((s) =>
			s.id === serviceId ? { ...s, teamMembers: newTeamMembers } : s
		);
	}

	async function addDispatchToService(service: EnrichedService) {
		if (!dispatchProfilePk || !dispatchProfileGlobalId) return;
		if (updatingServices.has(service.id)) return;

		const currentPks = service.teamMembers.map((m) => m.pk);
		const newPks = [...currentPks, dispatchProfilePk];
		const newTeamMembers = newPks.map((pk) => ({ pk }));
		const teamMemberGlobalIds = newPks.map((pk) => toGlobalId('TeamProfileType', pk));

		optimisticUpdates.set(service.id, newTeamMembers);
		updatingServices.add(service.id);

		try {
			await updateServiceStore.mutate({
				input: {
					id: service.id,
					teamMemberIds: teamMemberGlobalIds
				}
			});
			updateServiceTeamMembers(service.id, newTeamMembers);
			optimisticUpdates.delete(service.id);
		} catch (e) {
			optimisticUpdates.delete(service.id);
			error = e instanceof Error ? e.message : 'Failed to add dispatch';
		} finally {
			updatingServices.delete(service.id);
		}
	}

	async function removeDispatchFromService(service: EnrichedService) {
		if (!dispatchProfilePk) return;
		if (updatingServices.has(service.id)) return;

		const newPks = service.teamMembers.map((m) => m.pk).filter((pk) => pk !== dispatchProfilePk);
		const newTeamMembers = newPks.map((pk) => ({ pk }));
		const teamMemberGlobalIds = newPks.map((pk) => toGlobalId('TeamProfileType', pk));

		optimisticUpdates.set(service.id, newTeamMembers);
		updatingServices.add(service.id);

		try {
			await updateServiceStore.mutate({
				input: {
					id: service.id,
					teamMemberIds: teamMemberGlobalIds
				}
			});
			updateServiceTeamMembers(service.id, newTeamMembers);
			optimisticUpdates.delete(service.id);
			stagedTeamMembers.delete(service.id);
		} catch (e) {
			optimisticUpdates.delete(service.id);
			error = e instanceof Error ? e.message : 'Failed to remove dispatch';
		} finally {
			updatingServices.delete(service.id);
		}
	}

	async function removeNonDispatchMembers(service: EnrichedService) {
		if (!dispatchProfilePk) return;
		if (updatingServices.has(service.id)) return;

		const newTeamMembers = [{ pk: dispatchProfilePk }];
		const teamMemberGlobalIds = [toGlobalId('TeamProfileType', dispatchProfilePk)];

		optimisticUpdates.set(service.id, newTeamMembers);
		updatingServices.add(service.id);

		try {
			await updateServiceStore.mutate({
				input: {
					id: service.id,
					teamMemberIds: teamMemberGlobalIds
				}
			});
			updateServiceTeamMembers(service.id, newTeamMembers);
			optimisticUpdates.delete(service.id);
		} catch (e) {
			optimisticUpdates.delete(service.id);
			error = e instanceof Error ? e.message : 'Failed to remove team members';
		} finally {
			updatingServices.delete(service.id);
		}
	}

	// Staging functions
	function getAvailableTeamMembers(service: EnrichedService): { pk: string; name: string }[] {
		const assignedPks = new SvelteSet(service.teamMembers.map((m) => m.pk));
		const stagedPks = new SvelteSet(stagedTeamMembers.get(service.id) ?? []);

		return nonAdminTeamMembers
			.filter((p) => {
				const pk = fromGlobalId(p.id);
				if (!pk) return false;
				if (assignedPks.has(pk)) return false;
				return !stagedPks.has(pk);
			})
			.map((p) => ({
				pk: fromGlobalId(p.id)!,
				name: p.fullName
			}));
	}

	function getStagedTeamMemberDetails(serviceId: string): { pk: string; name: string }[] {
		const staged = stagedTeamMembers.get(serviceId) ?? [];
		return staged
			.map((pk) => {
				const profile = teamProfiles.find((p) => fromGlobalId(p.id) === pk);
				return profile ? { pk, name: profile.fullName } : null;
			})
			.filter((m): m is { pk: string; name: string } => m !== null);
	}

	function stageTeamMember(serviceId: string, memberPk: string) {
		const current = stagedTeamMembers.get(serviceId) ?? [];
		if (!current.includes(memberPk)) {
			stagedTeamMembers.set(serviceId, [...current, memberPk]);
		}
		openTeamMemberDropdown = null;
	}

	function unstageTeamMember(serviceId: string, memberPk: string) {
		const current = stagedTeamMembers.get(serviceId) ?? [];
		stagedTeamMembers.set(
			serviceId,
			current.filter((pk) => pk !== memberPk)
		);
	}

	function hasStagedMembers(serviceId: string): boolean {
		const staged = stagedTeamMembers.get(serviceId);
		return staged !== undefined && staged.length > 0;
	}

	async function submitStagedTeamMembers(service: EnrichedService) {
		const staged = stagedTeamMembers.get(service.id);
		if (!staged || staged.length === 0) return;
		if (updatingServices.has(service.id)) return;

		const currentPks = service.teamMembers.map((m) => m.pk);
		const newPks = [...currentPks, ...staged];
		const newTeamMembers = newPks.map((pk) => ({ pk }));
		const teamMemberGlobalIds = newPks.map((pk) => toGlobalId('TeamProfileType', pk));

		optimisticUpdates.set(service.id, newTeamMembers);
		updatingServices.add(service.id);

		try {
			await updateServiceStore.mutate({
				input: {
					id: service.id,
					teamMemberIds: teamMemberGlobalIds
				}
			});
			updateServiceTeamMembers(service.id, newTeamMembers);
			optimisticUpdates.delete(service.id);
			stagedTeamMembers.delete(service.id);
		} catch (e) {
			optimisticUpdates.delete(service.id);
			error = e instanceof Error ? e.message : 'Failed to assign team members';
		} finally {
			updatingServices.delete(service.id);
		}
	}

	// Date editing
	async function updateServiceDate(service: EnrichedService, newDate: string) {
		if (updatingServices.has(service.id)) return;

		updatingServices.add(service.id);
		editingDateServiceId = null;

		try {
			await updateServiceStore.mutate({
				input: {
					id: service.id,
					date: newDate
				}
			});
			await fetchServices();
		} catch (e) {
			error = e instanceof Error ? e.message : 'Failed to update date';
		} finally {
			updatingServices.delete(service.id);
		}
	}

	function startEditDate(serviceId: string) {
		editingDateServiceId = serviceId;
	}

	function cancelEditDate() {
		editingDateServiceId = null;
	}

	// Delete operations
	function promptDeleteService(service: EnrichedService) {
		serviceToDelete = {
			id: service.id,
			accountName: service.accountName,
			date: service.date
		};
		showDeleteModal = true;
	}

	async function confirmDeleteService() {
		if (!serviceToDelete) return;

		isDeleting = true;

		try {
			await deleteServiceStore.mutate({
				id: serviceToDelete.id
			});
			showDeleteModal = false;
			serviceToDelete = null;
			await fetchServices();
		} catch (e) {
			error = e instanceof Error ? e.message : 'Failed to delete service';
		} finally {
			isDeleting = false;
		}
	}

	function cancelDeleteService() {
		showDeleteModal = false;
		serviceToDelete = null;
	}

	// Selection functions
	function toggleServiceSelection(column: ColumnType, serviceId: string) {
		const set =
			column === 'unassigned'
				? selectedUnassigned
				: column === 'readyToAssign'
					? selectedReadyToAssign
					: selectedAssigned;
		if (set.has(serviceId)) {
			set.delete(serviceId);
		} else {
			set.add(serviceId);
		}
	}

	function selectAllInColumn(column: ColumnType) {
		const services =
			column === 'unassigned'
				? categorizedServices.unassigned
				: column === 'readyToAssign'
					? categorizedServices.readyToAssign
					: categorizedServices.assigned;
		const set =
			column === 'unassigned'
				? selectedUnassigned
				: column === 'readyToAssign'
					? selectedReadyToAssign
					: selectedAssigned;
		for (const service of services) {
			set.add(service.id);
		}
	}

	function clearColumnSelection(column: ColumnType) {
		const set =
			column === 'unassigned'
				? selectedUnassigned
				: column === 'readyToAssign'
					? selectedReadyToAssign
					: selectedAssigned;
		set.clear();
		if (column === 'readyToAssign') {
			bulkSelectedTeamMember = null;
			showBulkTeamMemberDropdown = false;
		}
	}

	function getSelectedServices(column: ColumnType): EnrichedService[] {
		const services =
			column === 'unassigned'
				? categorizedServices.unassigned
				: column === 'readyToAssign'
					? categorizedServices.readyToAssign
					: categorizedServices.assigned;
		const set =
			column === 'unassigned'
				? selectedUnassigned
				: column === 'readyToAssign'
					? selectedReadyToAssign
					: selectedAssigned;
		return services.filter((s) => set.has(s.id));
	}

	// Bulk operations
	async function bulkAddDispatch() {
		if (!dispatchProfilePk || !dispatchProfileGlobalId) return;
		const selected = getSelectedServices('unassigned');
		if (selected.length === 0) return;

		isBulkOperating = true;
		error = '';

		try {
			for (const service of selected) {
				if (updatingServices.has(service.id)) continue;

				const currentPks = service.teamMembers.map((m) => m.pk);
				const newPks = [...currentPks, dispatchProfilePk];
				const teamMemberGlobalIds = newPks.map((pk) => toGlobalId('TeamProfileType', pk));

				updatingServices.add(service.id);
				await updateServiceStore.mutate({
					input: {
						id: service.id,
						teamMemberIds: teamMemberGlobalIds
					}
				});
				updatingServices.delete(service.id);
			}
			selectedUnassigned.clear();
			await fetchServices();
		} catch (e) {
			error = e instanceof Error ? e.message : 'Failed to add dispatch to services';
		} finally {
			isBulkOperating = false;
		}
	}

	async function bulkAddTeamMember() {
		if (!bulkSelectedTeamMember || !dispatchProfilePk) return;
		const selected = getSelectedServices('readyToAssign');
		if (selected.length === 0) return;

		isBulkOperating = true;
		error = '';

		try {
			for (const service of selected) {
				if (updatingServices.has(service.id)) continue;

				const currentPks = service.teamMembers.map((m) => m.pk);
				const newPks = currentPks.includes(bulkSelectedTeamMember)
					? currentPks
					: [...currentPks, bulkSelectedTeamMember];
				const teamMemberGlobalIds = newPks.map((pk) => toGlobalId('TeamProfileType', pk));

				updatingServices.add(service.id);
				await updateServiceStore.mutate({
					input: {
						id: service.id,
						teamMemberIds: teamMemberGlobalIds
					}
				});
				updatingServices.delete(service.id);
			}
			selectedReadyToAssign.clear();
			bulkSelectedTeamMember = null;
			showBulkTeamMemberDropdown = false;
			await fetchServices();
		} catch (e) {
			error = e instanceof Error ? e.message : 'Failed to add team member to services';
		} finally {
			isBulkOperating = false;
		}
	}

	async function bulkRemoveNonDispatch() {
		if (!dispatchProfilePk) return;
		const selected = getSelectedServices('assigned');
		if (selected.length === 0) return;

		isBulkOperating = true;
		error = '';

		try {
			for (const service of selected) {
				if (updatingServices.has(service.id)) continue;

				const teamMemberGlobalIds = [toGlobalId('TeamProfileType', dispatchProfilePk)];

				updatingServices.add(service.id);
				await updateServiceStore.mutate({
					input: {
						id: service.id,
						teamMemberIds: teamMemberGlobalIds
					}
				});
				updatingServices.delete(service.id);
			}
			selectedAssigned.clear();
			await fetchServices();
		} catch (e) {
			error = e instanceof Error ? e.message : 'Failed to remove team members from services';
		} finally {
			isBulkOperating = false;
		}
	}

	// Dropdown management
	function closeAllDropdowns() {
		openTeamMemberDropdown = null;
		showBulkTeamMemberDropdown = false;
	}

	function setOpenTeamMemberDropdown(id: string | null) {
		openTeamMemberDropdown = id;
	}

	function toggleBulkTeamMemberDropdown() {
		showBulkTeamMemberDropdown = !showBulkTeamMemberDropdown;
	}

	function setBulkSelectedTeamMember(pk: string | null) {
		bulkSelectedTeamMember = pk;
	}

	// Clear error
	function clearError() {
		error = '';
	}

	// Set view mode
	function setViewMode(mode: ViewMode) {
		viewMode = mode;
	}

	return {
		// Initialization
		init,
		fetchServices,

		// State getters (using $derived for reactivity)
		get currentMonth() {
			return currentMonth;
		},
		get monthDisplay() {
			return monthDisplay;
		},
		get isLoading() {
			return isLoading;
		},
		get error() {
			return error;
		},
		get viewMode() {
			return viewMode;
		},
		get activeTab() {
			return activeTab;
		},
		get isBulkOperating() {
			return isBulkOperating;
		},
		get editingDateServiceId() {
			return editingDateServiceId;
		},
		get openTeamMemberDropdown() {
			return openTeamMemberDropdown;
		},
		get bulkSelectedTeamMember() {
			return bulkSelectedTeamMember;
		},
		get showBulkTeamMemberDropdown() {
			return showBulkTeamMemberDropdown;
		},
		get showDeleteModal() {
			return showDeleteModal;
		},
		get serviceToDelete() {
			return serviceToDelete;
		},
		get isDeleting() {
			return isDeleting;
		},
		get nonAdminTeamMembers() {
			return nonAdminTeamMembers;
		},

		// Derived data
		get categorizedServices() {
			return categorizedServices;
		},
		get groupedUnassigned() {
			return groupedUnassigned;
		},
		get groupedReadyToAssign() {
			return groupedReadyToAssign;
		},
		get groupedAssigned() {
			return groupedAssigned;
		},

		// Selection sets
		updatingServices,
		expandedUnassigned,
		expandedReadyToAssign,
		expandedAssigned,
		selectedUnassigned,
		selectedReadyToAssign,
		selectedAssigned,

		// Navigation
		previousMonth,
		nextMonth,
		goToCurrentMonth,
		previousTab,
		nextTab,

		// View controls
		setViewMode,
		resetExpandedGroups,
		toggleGroup,
		closeAllDropdowns,
		setOpenTeamMemberDropdown,
		toggleBulkTeamMemberDropdown,
		setBulkSelectedTeamMember,

		// Team member helpers
		getTeamMemberNames,
		getNonDispatchTeamMemberNames,
		getAvailableTeamMembers,
		getStagedTeamMemberDetails,
		hasStagedMembers,

		// Service operations
		addDispatchToService,
		removeDispatchFromService,
		removeNonDispatchMembers,
		stageTeamMember,
		unstageTeamMember,
		submitStagedTeamMembers,

		// Date editing
		updateServiceDate,
		startEditDate,
		cancelEditDate,

		// Delete operations
		promptDeleteService,
		confirmDeleteService,
		cancelDeleteService,

		// Selection operations
		toggleServiceSelection,
		selectAllInColumn,
		clearColumnSelection,
		getSelectedServices,

		// Bulk operations
		bulkAddDispatch,
		bulkAddTeamMember,
		bulkRemoveNonDispatch,

		// Error handling
		clearError
	};
}
