import {
  AssetsJSON,
  ComponentDescription,
  AssetPackage,
  ComponentGroup,
  TemplateDescription
} from './types';
import { isJSUrl, isCSSUrl } from '../utils';
import {
  emitter,
  EVENT_ASSETS_TEMPLATE_REMOVE,
  EVENT_ASSETS_TEMPLATE_ADD,
  EVENT_ASSETS_READY
} from './emitter';

function parseGroups(
  groups: ComponentGroup[],
  list: ComponentDescription[] | TemplateDescription[]
) {
  if (!groups) return [];
  const components = list || [];
  return groups.map((n) => {
    return {
      groupId: n.id,
      group: n.group,
      categories: n.categories.map((c) => {
        return {
          categoryId: c.id,
          category: c.category,
          components: components.filter(
            (component) =>
              component.groupId === n.id && component.categoryId === c.id
          )
        };
      })
    };
  });
}

function parseMap<T>(list: T[]) {
  const map: Record<string, T> = {};
  for (const c of list) {
    map[(c as any).name] = c;
  }
  return map;
}

export interface GroupComponents {
  groupId: string | number;
  group: string;
  categories: {
    categoryId: string | number;
    category: string;
    components: ComponentDescription[];
  }[];
}

export interface AssetsOptions {
  loader: () => Promise<AssetsJSON>;
}

export class Assets {
  groupComponents: GroupComponents[] = [];
  componentMap: Record<string, ComponentDescription> = {};
  components: ComponentDescription[] = [];
  packages: AssetPackage[] = [];
  groupTemplates: GroupComponents[] = [];
  templates: TemplateDescription[] = [];
  templateMap: Record<string, TemplateDescription> = {};
  templateGroups: ComponentGroup[] = [];
  isReady: boolean = false;
  constructor(options: AssetsOptions) {
    const { loader } = options;
    loader().then((json) => {
      this.components = json.components || [];
      this.templates = json.templates || [];
      this.templateGroups = json.templateGroups || [];
      this.packages = json.packages || [];
      this.groupComponents = parseGroups(
        json.groups || [],
        json.components || []
      );
      this.groupTemplates = parseGroups(
        this.templateGroups,
        json.templates || []
      );
      this.componentMap = parseMap<ComponentDescription>(this.components);
      this.templateMap = parseMap<TemplateDescription>(this.templates);
      emitter.emit(EVENT_ASSETS_READY);
      this.isReady = true;
    });
  }
  parsePackages() {
    const packages = this.packages;
    const scripts: string[] = [];
    const css: string[] = [];
    packages.forEach(({ urls }) => {
      urls?.forEach((url) => {
        if (isJSUrl(url)) {
          scripts.push(url);
        }
        if (isCSSUrl(url)) {
          css.push(url);
        }
      });
    });
    return {
      scripts,
      css
    };
  }

  removeTemplate(name: string) {
    delete this.templateMap[name];
    const index = this.templates.findIndex((n) => n.name == name);
    if (index > -1) {
      this.templates.splice(index, 1);
    }
    this.groupTemplates = parseGroups(this.templateGroups, this.templates);
    emitter.emit(EVENT_ASSETS_TEMPLATE_REMOVE, name);
  }

  addTemplate(template: TemplateDescription) {
    this.templateMap[template.name] = template;
    this.templates.push(template);
    this.groupTemplates = parseGroups(this.templateGroups, this.templates);
    emitter.emit(EVENT_ASSETS_TEMPLATE_ADD, template);
  }
}
