import { computed, makeObservable } from 'mobx';
import { FiltersMap, KeyedItem } from '@wix/bex-core';
import { TableState } from '../../state';
import { GroupBy, RenderSection, Section } from '../CollectionSectionHeader';

const SECTION_PREFIX = '$$$wix-patterns-sections$$$-';
const INVISIBLE_ROW_ID = `${SECTION_PREFIX}invisible`;

export class TableSectionsState<T, F extends FiltersMap> {
  table: TableState<T, F>;
  groupBy: GroupBy<T>;
  renderSection: RenderSection<T>;

  constructor(
    table: TableState<T, F>,
    groupBy: GroupBy<T>,
    renderSection: RenderSection<T>,
  ) {
    this.table = table;
    this.groupBy = groupBy;
    this.renderSection = renderSection;
    makeObservable(this, {
      sectionsKeyedItems: computed,
      sectionCounts: computed,
    });
  }

  private generateSectionId(firstItemId: string): string {
    return `${SECTION_PREFIX}${firstItemId}`;
  }

  private get computeSectionData(): {
    counts: Map<string, { count: number; isComplete: boolean }>;
    items: Map<string, T[]>;
    keyedItems: KeyedItem<T>[];
  } {
    const counts = new Map<string, { count: number; isComplete: boolean }>();
    const items = new Map<string, T[]>();
    const keyedItems: KeyedItem<T>[] = [];

    if (this.table.keyedItems.length === 0) {
      return { counts, items, keyedItems };
    }

    // Add invisible row for design purposes
    keyedItems.push({
      id: INVISIBLE_ROW_ID,
      key: INVISIBLE_ROW_ID,
      item: this.table.keyedItems[0].item,
      index: 0,
      pageIndex: 0,
      indexWithinPage: 0,
    });

    let currentSectionId: string | null = null;
    let currentSectionIndex: number = 0;
    let currentHeaderId: string | null = null;
    let count = 0;
    let currentSectionItems: T[] = [];

    for (const keyedItem of this.table.keyedItems) {
      const sectionId = this.groupBy(keyedItem.item);

      if (sectionId !== currentSectionId) {
        // Store count and items for previous section
        if (currentHeaderId) {
          counts.set(currentHeaderId, { count, isComplete: true });
          items.set(currentHeaderId, currentSectionItems);
        }

        // Start new section
        currentSectionId = sectionId;
        currentHeaderId = this.generateSectionId(
          `${sectionId}@${currentSectionIndex}`,
        );
        currentSectionIndex++;
        count = 1;
        currentSectionItems = [keyedItem.item];

        // Add section header to keyed items
        const sectionKeyedItem: KeyedItem<T> = {
          id: currentHeaderId,
          key: currentHeaderId,
          item: keyedItem.item,
          index: keyedItems.length,
          pageIndex: 0,
          indexWithinPage: keyedItems.length,
        };
        keyedItems.push(sectionKeyedItem);
      } else {
        count++;
        currentSectionItems.push(keyedItem.item);
      }

      // Add the actual data item
      keyedItems.push(keyedItem);
    }

    // Handle last section
    if (currentHeaderId) {
      const isLastSectionComplete = this.table.collection.fetchedLastPage;
      counts.set(currentHeaderId, { count, isComplete: isLastSectionComplete });
      items.set(currentHeaderId, currentSectionItems);
    }

    return { counts, items, keyedItems };
  }

  get sectionCounts(): Map<string, { count: number; isComplete: boolean }> {
    return this.computeSectionData.counts;
  }

  get sectionItems(): Map<string, T[]> {
    return this.computeSectionData.items;
  }

  get sectionsKeyedItems(): KeyedItem<T>[] {
    return this.computeSectionData.keyedItems;
  }

  getSectionWithCount(sectionKeyedItem: KeyedItem<T>): {
    sectionId: string;
    section: Section & { count: number; hasMore: boolean; items: T[] };
  } {
    const headerId = sectionKeyedItem.id;
    const sectionData = headerId
      ? this.sectionCounts.get(headerId) || { count: 0, isComplete: true }
      : { count: 0, isComplete: true };
    const sectionItemsList = headerId
      ? this.sectionItems.get(headerId) || []
      : [];

    const sectionId = this.groupBy(sectionKeyedItem.item);
    const section = this.renderSection(sectionId, sectionItemsList);

    return {
      sectionId,
      section: {
        ...section,
        count: sectionData.count,
        hasMore: !sectionData.isComplete,
        items: sectionItemsList,
      },
    };
  }

  isSectionRow(keyedItem: KeyedItem<T>): boolean {
    return keyedItem.id.startsWith(SECTION_PREFIX);
  }

  isInvisibleRow(keyedItem: KeyedItem<T>): boolean {
    return keyedItem.id === INVISIBLE_ROW_ID;
  }
}
