import { Category } from '../models/category-domain.model';

export class CategoryTreeUtils {
  static toTree(categories: Category[]): Category[] {
    const byId = new Map<number, Category>();

    for (const category of categories) {
      byId.set(category.id, { ...category, children: [] });
    }

    const roots: Category[] = [];

    for (const category of byId.values()) {
      if (category.parentId == null) {
        roots.push(category);
        continue;
      }

      const parent = byId.get(category.parentId);
      if (parent) {
        parent.children.push(category);
      } else {
        roots.push(category);
      }
    }

    this.sortTree(roots);
    return roots;
  }

  static flattenTree(categories: Category[]): Category[] {
    return categories.flatMap(category => [category, ...this.flattenTree(category.children)]);
  }

  static findById(categories: Category[], categoryId: number): Category | undefined {
    return this.flattenTree(categories).find(category => category.id === categoryId);
  }

  static getRootCategories(categories: Category[]): Category[] {
    return this.toTree(categories);
  }

  static getChildren(categories: Category[], parentId: number): Category[] {
    const tree = this.toTree(categories);
    return this.findById(tree, parentId)?.children ?? [];
  }

  static getParent(categories: Category[], categoryId: number): Category | undefined {
    const category = categories.find(item => item.id === categoryId);
    return category?.parentId == null
      ? undefined
      : categories.find(item => item.id === category.parentId);
  }

  static getBreadcrumb(categories: Category[], categoryId: number): Category[] {
    const byId = new Map(categories.map(category => [category.id, category]));
    const breadcrumb: Category[] = [];
    let current = byId.get(categoryId);

    while (current) {
      breadcrumb.unshift(current);
      current = current.parentId == null ? undefined : byId.get(current.parentId);
    }

    return breadcrumb;
  }

  static isLeaf(category: Category): boolean {
    return category.children.length === 0;
  }

  private static sortTree(categories: Category[]): void {
    categories.sort((a, b) => a.priority - b.priority || a.id - b.id);
    categories.forEach(category => this.sortTree(category.children));
  }
}