import type { SkillCategory, SkillCategoryDraft } from './types';

export interface FlatSkillCategory extends SkillCategory {
  depth: number;
}

/**
 * Flattens a category tree while retaining its display depth.
 */
export function flattenSkillCategories(
  categories: SkillCategory[],
  depth = 0,
): FlatSkillCategory[] {
  return categories.flatMap((category) => [
    { ...category, depth },
    ...flattenSkillCategories(category.children || [], depth + 1),
  ]);
}

/**
 * Builds the writable category DTO without leaking server-managed fields.
 */
export function buildSkillCategoryPayload(
  category: SkillCategoryDraft,
  includeId: boolean,
): SkillCategoryDraft {
  const parentId =
    category.parentId === '' || category.parentId === null
      ? undefined
      : category.parentId;
  const payload: SkillCategoryDraft = {
    categoryName: category.categoryName.trim(),
    parentId,
    sortNo:
      typeof category.sortNo === 'number' && Number.isFinite(category.sortNo)
        ? Math.trunc(category.sortNo)
        : 0,
    status: category.status === 0 ? 0 : 1,
  };
  if (includeId) payload.id = category.id;
  return payload;
}

/**
 * Returns whether a category can accept another level within the three-level limit.
 */
export function canCreateSkillSubcategory(category: FlatSkillCategory) {
  return (category.levelNo ?? category.depth + 1) < 3;
}

/**
 * Returns legal parent choices, excluding cycles and moves that exceed three levels.
 */
export function getSkillCategoryParentOptions(
  categories: SkillCategory[],
  editingId?: number | string,
): FlatSkillCategory[] {
  const editing = editingId
    ? findSkillCategory(categories, editingId)
    : undefined;
  const excludedIds = editing ? collectCategoryIds(editing) : new Set<string>();
  const subtreeHeight = editing ? getSubtreeHeight(editing) : 1;

  return flattenSkillCategories(categories).filter((category) => {
    if (excludedIds.has(String(category.id))) return false;
    const parentLevel = category.levelNo ?? category.depth + 1;
    return parentLevel + subtreeHeight <= 3;
  });
}

function findSkillCategory(
  categories: SkillCategory[],
  id: number | string,
): SkillCategory | undefined {
  for (const category of categories) {
    if (String(category.id) === String(id)) return category;
    const child = findSkillCategory(category.children || [], id);
    if (child) return child;
  }
  return undefined;
}

function collectCategoryIds(
  category: SkillCategory,
  result = new Set<string>(),
) {
  result.add(String(category.id));
  for (const child of category.children || [])
    collectCategoryIds(child, result);
  return result;
}

function getSubtreeHeight(category: SkillCategory): number {
  if (!category.children?.length) return 1;
  return (
    1 + Math.max(...category.children.map((child) => getSubtreeHeight(child)))
  );
}
