import { schema, denormalize as baseDenormalize } from 'normalizr';

import { EntityWithBindings6, PageDSL8, WidgetProps, WidgetProps8, WidgetTypes } from '../types/index.js';
import { getNextEntityName } from './string.js';

const widgetSchema = new schema.Entity('canvasWidgets', {}, { idAttribute: 'widgetId' });
widgetSchema.define({ children: [widgetSchema] });

export type FlattenedWidgetProps = Omit<WidgetProps, 'children'> & {
  type: keyof typeof WidgetTypes;
  children?: string[];
} & EntityWithBindings6;

const PAGE_WIDGET_ID = '0';

type WidgetMap = Record<string, FlattenedWidgetProps>;

/**
 * Fixes invalid DSL from previous bugs on initial load.
 *  1. removes duplicate IDs
 *  2. de-duplicates identical Section and Canvas names because their names don't matter
 *  3. removes timers and stateVars from the page widget, as they are processed separately
 */
export function normalize<T = WidgetMap>(
  dsl?: PageDSL8
): {
  entities: {
    canvasWidgets: T;
  };
  result: string;
} {
  if (!dsl) return { entities: { canvasWidgets: {} as T }, result: '' };

  const flattenedWidgets = {} as T;
  const previouslySeenCanvasNames = new Set<string>();

  function flattenWidget(widget: WidgetProps8): void {
    if (flattenedWidgets[widget.widgetId]) return;
    if (widget.children) {
      flattenedWidgets[widget.widgetId] = {
        ...widget,
        children: Array.from(new Set(widget.children?.map((child) => child.widgetId)))
      } as FlattenedWidgetProps;
    } else {
      flattenedWidgets[widget.widgetId] = widget as FlattenedWidgetProps;
    }

    if (widget.widgetId === PAGE_WIDGET_ID) {
      // Extracted separately
      delete widget.stateVars;
      delete widget.timers;
      delete widget.events;
    }

    if (
      (widget.type === WidgetTypes.CANVAS_WIDGET || widget.type === WidgetTypes.SECTION_WIDGET) &&
      previouslySeenCanvasNames.has(widget.widgetName)
    ) {
      const newName = getNextEntityName(widget.widgetName, Array.from(previouslySeenCanvasNames));
      flattenedWidgets[widget.widgetId].widgetName = newName;
      previouslySeenCanvasNames.add(newName);
    } else {
      previouslySeenCanvasNames.add(widget.widgetName);
    }
    if (widget.children) {
      widget.children.forEach(flattenWidget);
    }
  }
  flattenWidget(dsl);

  return {
    entities: { canvasWidgets: flattenedWidgets },
    result: dsl.widgetId
  };
}

export function denormalize<T = WidgetMap>(pageWidgetId: string, entities: { canvasWidgets: T }): PageDSL8 {
  return baseDenormalize(pageWidgetId, widgetSchema, entities);
}
