import { CategoryDto, CategoryNameDto } from '../dto/category.dto';
import { Category, CategoryTranslation } from '../models/category-domain.model';

export class CategoryMapper {
  static toDomainList(dtos: CategoryDto[]): Category[] {
    const byId = new Map<number, Category>();

    for (const dto of dtos) {
      for (const category of this.flattenDto(dto)) {
        byId.set(category.id, category);
      }
    }

    return Array.from(byId.values())
      .filter(category => category.visible)
      .sort((a, b) => a.priority - b.priority || a.id - b.id);
  }

  static toDomain(dto: CategoryDto, fallbackParentId: number | null = null): Category | null {
    const id = this.toNumber(dto.categoryID ?? dto.id ?? dto.categoryId);

    if (id == null) {
      return null;
    }

    const parentId = this.toNumber(dto.parentID ?? dto.parentId) ?? fallbackParentId;
    const translations = this.toTranslations(dto.names ?? []);
    const fallbackTitle = this.firstTranslatedTitle(translations) || dto.name || '';

    return {
      id,
      parentId: parentId && parentId !== 0 ? parentId : null,
      title: fallbackTitle,
      icon: dto.icon ?? dto.img,
      priority: dto.priority ?? 0,
      visible: dto.visible ?? true,
      itemCount: dto.itemCount ?? 0,
      children: [],
      translations,
    };
  }

  private static flattenDto(dto: CategoryDto, fallbackParentId: number | null = null): Category[] {
    const category = this.toDomain(dto, fallbackParentId);
    const currentParentId = category?.id ?? fallbackParentId;
    const nested = (dto.subcategories ?? []).flatMap(child => this.flattenDto(child, currentParentId));

    return category ? [category, ...nested] : nested;
  }

  private static toTranslations(names: CategoryNameDto[]): Record<string, CategoryTranslation> {
    return names.reduce<Record<string, CategoryTranslation>>((acc, entry) => {
      const language = this.normalizeLanguage(entry.language);
      const title = entry.value ?? entry.valuue ?? '';

      if (language && title) {
        acc[language] = { title };
      }

      return acc;
    }, {});
  }

  private static normalizeLanguage(language?: string): string {
    const normalized = language?.toLowerCase();

    if (normalized === 'am') {
      return 'hy';
    }

    return normalized ?? '';
  }

  private static firstTranslatedTitle(translations: Record<string, CategoryTranslation>): string {
    return translations['ru']?.title
      ?? translations['en']?.title
      ?? translations['hy']?.title
      ?? Object.values(translations)[0]?.title
      ?? '';
  }

  private static toNumber(value: unknown): number | null {
    if (typeof value === 'number' && Number.isFinite(value)) {
      return value;
    }

    if (typeof value === 'string' && value.trim()) {
      const parsed = Number(value);
      return Number.isFinite(parsed) ? parsed : null;
    }

    return null;
  }
}