import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, IsNull } from 'typeorm';
import { AssetFolder } from '../entities/asset-folder.entity';
import { CreateFolderDto, UpdateFolderDto } from '../dto';
import { TenantAwareService } from '@/shared/services/tenant-aware.service';

@Injectable()
export class FolderService extends TenantAwareService<AssetFolder> {
  constructor(
    @InjectRepository(AssetFolder)
    private readonly folderRepository: Repository<AssetFolder>,
  ) {
    super(folderRepository);
  }

  async findAll(tenantId: string, brandId?: string): Promise<AssetFolder[]> {
    const where: any = { tenant_id: tenantId, deleted_at: IsNull() };

    if (brandId) {
      where.brand_id = brandId;
    }

    return this.folderRepository.find({
      where,
      relations: ['children'],
      order: { sort_order: 'ASC', name: 'ASC' },
    });
  }

  async findRootFolders(tenantId: string, brandId?: string): Promise<AssetFolder[]> {
    const where: any = {
      tenant_id: tenantId,
      parent_id: IsNull(),
      deleted_at: IsNull(),
    };

    if (brandId) {
      where.brand_id = brandId;
    }

    return this.folderRepository.find({
      where,
      relations: ['children'],
      order: { sort_order: 'ASC', name: 'ASC' },
    });
  }

  async findById(tenantId: string, id: string): Promise<AssetFolder> {
    const folder = await this.folderRepository.findOne({
      where: { id, tenant_id: tenantId, deleted_at: IsNull() },
      relations: ['parent', 'children', 'brand'],
    });

    if (!folder) {
      throw new NotFoundException(`Folder with ID ${id} not found`);
    }

    return folder;
  }

  async findBySlug(tenantId: string, slug: string): Promise<AssetFolder> {
    const folder = await this.folderRepository.findOne({
      where: { slug, tenant_id: tenantId, deleted_at: IsNull() },
      relations: ['parent', 'children'],
    });

    if (!folder) {
      throw new NotFoundException(`Folder with slug ${slug} not found`);
    }

    return folder;
  }

  async create(tenantId: string, dto: CreateFolderDto): Promise<AssetFolder> {
    const slug = dto.slug || this.generateSlug(dto.name);

    // Check for unique slug
    const existing = await this.folderRepository.findOne({
      where: { tenant_id: tenantId, slug, deleted_at: IsNull() },
    });

    if (existing) {
      throw new BadRequestException(`Folder with slug "${slug}" already exists`);
    }

    let level = 0;
    let path = `/${slug}`;
    let parent: AssetFolder | null = null;

    if (dto.parent_id) {
      parent = await this.findById(tenantId, dto.parent_id);
      level = parent.level + 1;
      path = `${parent.path}/${slug}`;
    }

    const folder = this.folderRepository.create({
      ...dto,
      slug,
      level,
      path,
      tenant_id: tenantId,
    });

    return this.folderRepository.save(folder);
  }

  async update(tenantId: string, id: string, dto: UpdateFolderDto): Promise<AssetFolder> {
    const folder = await this.findById(tenantId, id);

    if (dto.slug && dto.slug !== folder.slug) {
      const existing = await this.folderRepository.findOne({
        where: { tenant_id: tenantId, slug: dto.slug, deleted_at: IsNull() },
      });

      if (existing && existing.id !== id) {
        throw new BadRequestException(`Folder with slug "${dto.slug}" already exists`);
      }
    }

    if (dto.parent_id && dto.parent_id !== folder.parent_id) {
      // Prevent circular reference
      if (dto.parent_id === id) {
        throw new BadRequestException('Cannot set folder as its own parent');
      }

      const newParent = await this.findById(tenantId, dto.parent_id);
      folder.level = newParent.level + 1;
      folder.path = `${newParent.path}/${folder.slug}`;
    }

    Object.assign(folder, dto);

    return this.folderRepository.save(folder);
  }

  async softDelete(tenantId: string, id: string): Promise<void> {
    const folder = await this.findById(tenantId, id);

    // Check if folder has children
    const children = await this.folderRepository.find({
      where: { tenant_id: tenantId, parent_id: id, deleted_at: IsNull() },
    });

    if (children.length > 0) {
      throw new BadRequestException('Cannot delete folder with subfolders');
    }

    folder.deleted_at = new Date();
    await this.folderRepository.save(folder);
  }

  async getTree(tenantId: string, brandId?: string): Promise<AssetFolder[]> {
    const rootFolders = await this.findRootFolders(tenantId, brandId);
    return this.buildTree(rootFolders, tenantId);
  }

  private async buildTree(folders: AssetFolder[], tenantId: string): Promise<AssetFolder[]> {
    for (const folder of folders) {
      if (folder.children && folder.children.length > 0) {
        folder.children = await this.buildTree(folder.children, tenantId);
      }
    }
    return folders;
  }

  private generateSlug(name: string): string {
    return name
      .toLowerCase()
      .normalize('NFD')
      .replace(/[\u0300-\u036f]/g, '')
      .replace(/[^a-z0-9]+/g, '-')
      .replace(/(^-|-$)/g, '');
  }
}
