import { IRenderer, IRenderContext } from './IGroupingRenderer';
import { buildPipeline } from './RenderBuilder';
import { EventRenderer } from '../features/event/EventRenderer';
import { ScheduleRenderer } from '../features/schedule/ScheduleRenderer';
import { HeaderDrawerRenderer } from '../features/headerdrawer/HeaderDrawerRenderer';
import { ViewConfig, GroupingConfig } from './ViewConfig';
import { FilterTemplate } from './FilterTemplate';
import { DateService } from './DateService';
import { IEntityService } from '../storage/IEntityService';
import { ISync } from '../types/CalendarTypes';

export class CalendarOrchestrator {
  constructor(
    private allRenderers: IRenderer[],
    private eventRenderer: EventRenderer,
    private scheduleRenderer: ScheduleRenderer,
    private headerDrawerRenderer: HeaderDrawerRenderer,
    private dateService: DateService,
    private entityServices: IEntityService<ISync>[]
  ) {}

  async render(viewConfig: ViewConfig, container: HTMLElement): Promise<void> {
    const headerContainer = container.querySelector('swp-calendar-header') as HTMLElement;
    const columnContainer = container.querySelector('swp-day-columns') as HTMLElement;
    if (!headerContainer || !columnContainer) {
      throw new Error('Missing swp-calendar-header or swp-day-columns');
    }

    // Byg filter fra viewConfig
    const filter: Record<string, string[]> = {};
    for (const grouping of viewConfig.groupings) {
      filter[grouping.type] = grouping.values;
    }

    // Byg FilterTemplate fra viewConfig groupings (kun de med idProperty)
    const filterTemplate = new FilterTemplate(this.dateService);
    for (const grouping of viewConfig.groupings) {
      if (grouping.idProperty) {
        filterTemplate.addField(grouping.idProperty, grouping.derivedFrom);
      }
    }

    // Resolve belongsTo relations (e.g., team.resourceIds)
    const { parentChildMap, childType } = await this.resolveBelongsTo(viewConfig.groupings, filter);

    const context: IRenderContext = { headerContainer, columnContainer, filter, groupings: viewConfig.groupings, parentChildMap, childType };

    // Clear
    headerContainer.innerHTML = '';
    columnContainer.innerHTML = '';

    // Sæt data-levels attribut for CSS grid-row styling
    const levels = viewConfig.groupings.map(g => g.type).join(' ');
    headerContainer.dataset.levels = levels;

    // Vælg renderers baseret på groupings types
    const activeRenderers = this.selectRenderers(viewConfig);

    // Byg og kør pipeline
    const pipeline = buildPipeline(activeRenderers);
    await pipeline.run(context);

    // Render schedule unavailable zones (før events)
    await this.scheduleRenderer.render(container, filter);

    // Render timed events in grid (med filterTemplate til matching)
    await this.eventRenderer.render(container, filter, filterTemplate);

    // Render allDay events in header drawer (med filterTemplate til matching)
    await this.headerDrawerRenderer.render(container, filter, filterTemplate);
  }

  private selectRenderers(viewConfig: ViewConfig): IRenderer[] {
    const types = viewConfig.groupings.map(g => g.type);
    // Sortér renderers i samme rækkefølge som viewConfig.groupings
    return types
      .map(type => this.allRenderers.find(r => r.type === type))
      .filter((r): r is IRenderer => r !== undefined);
  }

  /**
   * Resolve belongsTo relations to build parent-child map
   * e.g., belongsTo: 'team.resourceIds' → { team1: ['EMP001', 'EMP002'], team2: [...] }
   * Also returns the childType (the grouping type that has belongsTo)
   */
  private async resolveBelongsTo(
    groupings: GroupingConfig[],
    filter: Record<string, string[]>
  ): Promise<{ parentChildMap?: Record<string, string[]>; childType?: string }> {
    // Find grouping with belongsTo
    const childGrouping = groupings.find(g => g.belongsTo);
    if (!childGrouping?.belongsTo) return {};

    // Parse belongsTo: 'team.resourceIds'
    const [entityType, property] = childGrouping.belongsTo.split('.');
    if (!entityType || !property) return {};

    // Get parent IDs from filter
    const parentIds = filter[entityType] || [];
    if (parentIds.length === 0) return {};

    // Find service dynamisk baseret på entityType (ingen hardcoded type check)
    const service = this.entityServices.find(s =>
      s.entityType.toLowerCase() === entityType
    );
    if (!service) return {};

    // Hent alle entities og filtrer på parentIds
    const allEntities = await service.getAll();
    const entities = allEntities.filter(e =>
      parentIds.includes((e as unknown as Record<string, unknown>).id as string)
    );

    // Byg parent-child map
    const map: Record<string, string[]> = {};
    for (const entity of entities) {
      const entityRecord = entity as unknown as Record<string, unknown>;
      const children = (entityRecord[property] as string[]) || [];
      map[entityRecord.id as string] = children;
    }

    return { parentChildMap: map, childType: childGrouping.type };
  }
}
