import { SegmentationGroupType } from '@material-hu/components/composed-components/peopleExperience/types';

import {
  type FilterAction,
  type FilterState,
  type GroupMetadata,
  type SelectAllState,
  type SerializedFilterState,
} from './types';

// ============================================================================
// REDUCER
// ============================================================================

export function filterReducer(
  state: FilterState,
  action: FilterAction,
): FilterState {
  switch (action.type) {
    case 'TOGGLE_ITEM': {
      const { groupId, itemId } = action;
      const hasSelectAll = state.selectAllGroups.has(groupId);

      if (hasSelectAll) {
        // "Exclude mode": toggle in deselectedItems
        const deselected = new Map(state.deselectedItems);

        const groupDeselected = new Set(deselected.get(groupId) || []);

        if (groupDeselected.has(itemId)) {
          groupDeselected.delete(itemId);
        } else {
          groupDeselected.add(itemId);
        }

        deselected.set(groupId, groupDeselected);
        return {
          ...state,
          deselectedItems: deselected,
          isDirty: true,
        };
      } else {
        // Normal mode: toggle in selectedItems
        const selected = new Map(state.selectedItems);

        const groupSelected = new Set(selected.get(groupId) || []);

        if (groupSelected.has(itemId)) {
          groupSelected.delete(itemId);
        } else {
          groupSelected.add(itemId);
        }

        selected.set(groupId, groupSelected);

        return {
          ...state,
          selectedItems: selected,
          isDirty: true,
        };
      }
    }

    case 'SELECT_ALL_GROUP': {
      const { groupId } = action;

      // Activate select all for the group
      const selectAll = new Set(state.selectAllGroups);
      selectAll.add(groupId);

      // Clear deselectedItems for this group
      const deselected = new Map(state.deselectedItems);
      deselected.set(groupId, new Set());

      // Clear selectedItems for this group (no longer used)
      const selected = new Map(state.selectedItems);
      selected.set(groupId, new Set());

      return {
        ...state,
        selectedItems: selected,
        selectAllGroups: selectAll,
        deselectedItems: deselected,
        isDirty: true,
      };
    }

    case 'DESELECT_ALL_GROUP': {
      const { groupId } = action;

      // Deactivate select all
      const selectAll = new Set(state.selectAllGroups);
      selectAll.delete(groupId);

      // Clear selectedItems
      const selected = new Map(state.selectedItems);
      selected.set(groupId, new Set());

      // Clear deselectedItems
      const deselected = new Map(state.deselectedItems);
      deselected.set(groupId, new Set());

      return {
        ...state,
        selectedItems: selected,
        selectAllGroups: selectAll,
        deselectedItems: deselected,
        isDirty: true,
      };
    }

    case 'SET_GROUP_EXPANSION': {
      const { groupId, expanded: isExpanded } = action;
      const expanded = new Set(state.expandedGroups);

      if (isExpanded) {
        expanded.add(groupId);
      } else {
        expanded.delete(groupId);
      }

      return {
        ...state,
        expandedGroups: expanded,
      };
    }

    case 'REGISTER_GROUP_TYPE': {
      const { groupId, groupType } = action;
      const groupsByType = new Map(state.groupsByType);

      const typeSet = new Set(groupsByType.get(groupType) || []);
      typeSet.add(groupId);
      groupsByType.set(groupType, typeSet);

      return {
        ...state,
        groupsByType,
      };
    }

    case 'INITIALIZE_FILTERS': {
      const { filters } = action;

      return {
        selectedItems: new Map(
          Object.entries(filters.selectedItems).map(([groupId, itemIds]) => [
            Number(groupId),
            new Set(itemIds),
          ]),
        ),
        selectAllGroups: new Set(filters.selectAllGroups),
        deselectedItems: new Map(
          Object.entries(filters.deselectedItems).map(([groupId, itemIds]) => [
            Number(groupId),
            new Set(itemIds),
          ]),
        ),
        groupsByType: new Map(
          Object.entries(filters.groupsByType).map(([type, groupIds]) => [
            type as SegmentationGroupType,
            new Set(groupIds),
          ]),
        ),
        expandedGroups: state.expandedGroups,
        isDirty: false,
      };
    }

    case 'RESET_ALL': {
      return {
        selectedItems: new Map(),
        selectAllGroups: new Set(),
        deselectedItems: new Map(),
        expandedGroups: new Set(),
        groupsByType: state.groupsByType,
        isDirty: true,
      };
    }

    default:
      return state;
  }
}

// ============================================================================
// INITIAL STATE
// ============================================================================

export function getInitialFilters(): SerializedFilterState {
  return {
    selectedItems: {},
    selectAllGroups: [],
    deselectedItems: {},
    groupsByType: {
      [SegmentationGroupType.AGE]: [],
      [SegmentationGroupType.SENIORITY]: [],
      [SegmentationGroupType.SEGMENTATION_GROUP]: [],
    },
  };
}

// ============================================================================
// HELPER FUNCTIONS
// ============================================================================

/**
 * Check if an item is selected
 */
export function isItemSelected(
  state: FilterState,
  groupId: number,
  itemId: number,
): boolean {
  // If the group has "select all" active
  if (state.selectAllGroups.has(groupId)) {
    // It is selected EXCEPT if it is in deselectedItems
    const deselected = state.deselectedItems.get(groupId);
    return !deselected?.has(itemId);
  }

  // Normal behavior: look in selectedItems
  const selected = state.selectedItems.get(groupId);
  return selected?.has(itemId) ?? false;
}

/**
 * Gets the state of the "Select All" checkbox for the UI.
 * Considers the group's total items and how many are selected/deselected.
 */
export function getSelectAllState(
  state: FilterState,
  groupId: number,
  groupMetadata: GroupMetadata | null,
): SelectAllState {
  const hasSelectAll = state.selectAllGroups.has(groupId);
  const deselected = state.deselectedItems.get(groupId);
  const selected = state.selectedItems.get(groupId);

  // Case 1: Select All active
  if (hasSelectAll) {
    const deselectedCount = deselected?.size || 0;

    if (deselectedCount > 0) {
      return 'indeterminate';
    }
    return 'all';
  }

  // Case 2: Without Select All
  const selectedCount = selected?.size || 0;

  if (selectedCount === 0) {
    return 'none';
  }

  // If we don't have metadata and there are selections, it is indeterminate
  if (!groupMetadata) {
    return selectedCount > 0 ? 'indeterminate' : 'none';
  }

  // If all items of the group are manually selected
  if (selectedCount === groupMetadata.indexCount) {
    return 'all';
  }

  // Some selected, but not all
  return 'indeterminate';
}

/**
 * Gets the count of selected items in a group
 */
export function getSelectedCount(
  state: FilterState,
  groupId: number,
  groupMetadata: GroupMetadata | null,
): number {
  const hasSelectAll = state.selectAllGroups.has(groupId);

  if (hasSelectAll) {
    // With select all: total - deselected
    const totalCount = groupMetadata?.indexCount || 0;
    const deselectedCount = state.deselectedItems.get(groupId)?.size || 0;
    return totalCount - deselectedCount;
  }

  // Without select all: count selected
  return state.selectedItems.get(groupId)?.size || 0;
}

/**
 * Returns the indexCount of a group by its groupId.
 */
export function getGroupIndexCount(
  groupMetadata: GroupMetadata[],
  groupId: number,
): number {
  const group = groupMetadata.find(metadata => metadata.id === groupId);
  return group ? group.indexCount : 0;
}

/**
 * Returns the items from groupMetadata that are currently selected for the given group.
 * - If selectAll is active for the group, returns all indexItems except those in deselectedItems.
 * - Else, returns the items in indexItems that are present in selectedItems for the group.
 */
export function getSelectedItemsFromMetadata(
  state: FilterState,
  groupId: number,
  groupMetadata: GroupMetadata | null,
): { id: number; name: string }[] {
  if (!groupMetadata) return [];
  const { indexItems } = groupMetadata;

  if (state.selectAllGroups.has(groupId)) {
    const deselectedIds = state.deselectedItems.get(groupId);
    return indexItems.filter(item => !deselectedIds?.has(item.id));
  }

  const selectedIds = state.selectedItems.get(groupId);
  return indexItems.filter(item => selectedIds?.has(item.id));
}

/**
 * Serializes the state into an object
 */
export function serializeFilterState(
  state: FilterState,
): SerializedFilterState {
  return {
    selectedItems: Object.fromEntries(
      Array.from(state.selectedItems.entries()).map(([groupId, itemIds]) => [
        groupId,
        Array.from(itemIds),
      ]),
    ),
    selectAllGroups: Array.from(state.selectAllGroups),
    deselectedItems: Object.fromEntries(
      Array.from(state.deselectedItems.entries()).map(([groupId, itemIds]) => [
        groupId,
        Array.from(itemIds),
      ]),
    ),
    groupsByType: Object.fromEntries(
      Array.from(state.groupsByType.entries()).map(([type, groupIds]) => [
        type,
        Array.from(groupIds),
      ]),
    ) as Record<SegmentationGroupType, number[]>,
  };
}
