import {
  patchState,
  signalStore,
  type,
  withComputed,
  withHooks,
  withMethods,
} from '@ngrx/signals';
import {
  addEntity,
  entityConfig,
  setAllEntities,
  updateEntity,
  withEntities,
} from '@ngrx/signals/entities';
import { SessionStorageProvider, withStorage } from '@isa/core/storage';
import { Item } from '@isa/catalogue/data-access';
import { computed, effect, inject } from '@angular/core';
import { TabService } from '@isa/core/tabs';

export interface RewardCatalogEntity {
  tabId: number;
  items: Item[];
  hits: number;
  selectedItems: Record<number, Item>;
}

const config = entityConfig({
  entity: type<RewardCatalogEntity>(),
  selectId: (e) => e.tabId,
});

// TODO: Soll ausgetauscht werden durch einen Store der die TabId und SelectedItems generisch pro Tab im eingesetzten Feature verwaltet
// Der Store soll außerdem keine Items halten
export const RewardCatalogStore = signalStore(
  { providedIn: 'root' },
  withStorage(
    'reward-checkout-data-access.reward-catalog-store',
    SessionStorageProvider,
  ),
  withEntities<RewardCatalogEntity>(config),
  withComputed((store, tabService = inject(TabService)) => {
    const activeTabId = computed(() => tabService.activatedTabId());
    const activeEntity = computed<RewardCatalogEntity | undefined>(() => {
      const pid = activeTabId();
      if (pid == null) return undefined;
      return store.entities().find((e) => e.tabId === pid);
    });
    return {
      activeTabId,
      items: computed(() => activeEntity()?.items ?? []),
      hits: computed(() => activeEntity()?.hits ?? 0),
      selectedItems: computed(
        () => activeEntity()?.selectedItems ?? ({} as Record<number, Item>),
      ),
    } as const;
  }),
  withMethods((store) => {
    const ensureEntity = (tabId: number) => {
      const entity = store.entities().find((e) => e.tabId === tabId);
      if (!entity) {
        const newEntity: RewardCatalogEntity = {
          tabId,
          items: [],
          hits: 0,
          selectedItems: {},
        };
        patchState(store, addEntity(newEntity, config));
      }
    };
    return {
      setItems(items: Item[], hits: number) {
        const pid = store.activeTabId();
        if (pid == null) return;
        ensureEntity(pid);
        patchState(
          store,
          updateEntity(
            {
              id: pid,
              changes: { items, hits },
            },
            config,
          ),
        );
        store.saveToStorage();
      },
      updateItems(items: Item[], hits: number) {
        const pid = store.activeTabId();
        if (pid == null) return;
        ensureEntity(pid);
        const existing = store.entities().find((e) => e.tabId === pid);
        const merged = existing ? [...(existing.items ?? []), ...items] : items;
        patchState(
          store,
          updateEntity(
            {
              id: pid,
              changes: { items: merged, hits },
            },
            config,
          ),
        );
        store.saveToStorage();
      },
      selectItem(itemId: number, item: Item) {
        const pid = store.activeTabId();
        if (pid == null) return;
        ensureEntity(pid);
        const entity = store.entities().find((e) => e.tabId === pid)!;
        const selectedItems = { ...entity.selectedItems, [itemId]: item };
        patchState(
          store,
          updateEntity({ id: pid, changes: { selectedItems } }, config),
        );
      },
      removeItem(itemId: number) {
        const pid = store.activeTabId();
        if (pid == null) return;
        const entity = store.entities().find((e) => e.tabId === pid);
        if (!entity) return;
        const selectedItems = { ...entity.selectedItems };
        delete selectedItems[itemId];
        patchState(
          store,
          updateEntity({ id: pid, changes: { selectedItems } }, config),
        );
      },
      clearSelectedItems() {
        const pid = store.activeTabId();
        if (pid == null) return;
        const entity = store.entities().find((e) => e.tabId === pid);
        if (!entity) return;
        if (Object.keys(entity.selectedItems).length === 0) return;
        patchState(
          store,
          updateEntity({ id: pid, changes: { selectedItems: {} } }, config),
        );
      },
      clearState() {
        patchState(store, setAllEntities<RewardCatalogEntity>([], config));
        store.saveToStorage();
      },
      removeAllEntitiesByTabId(tabId: number) {
        const remaining = store.entities().filter((e) => e.tabId !== tabId);
        patchState(
          store,
          setAllEntities<RewardCatalogEntity>(remaining, config),
        );
        store.saveToStorage();
      },
    };
  }),
  withHooks((store, tabService = inject(TabService)) => ({
    onInit() {
      effect(() => {
        const tabIds = tabService.ids();
        const orphan = store.entities().find((e) => !tabIds.includes(e.tabId));
        if (orphan) {
          store.removeAllEntitiesByTabId(orphan.tabId);
        }
      });
    },
  })),
);
