import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, IsNull } from 'typeorm';
import { Project, ProjectStatus } from '../entities/project.entity';
import { CreateProjectDto, UpdateProjectDto } from '../dto';
import { TenantAwareService } from '@/shared/services/tenant-aware.service';
import { PaginationParams, PaginatedResult } from '@/shared/dto/pagination.dto';

@Injectable()
export class ProjectService extends TenantAwareService<Project> {
  constructor(
    @InjectRepository(Project)
    private readonly projectRepository: Repository<Project>,
  ) {
    super(projectRepository);
  }

  async findAllPaginated(
    tenantId: string,
    pagination: PaginationParams & { brandId?: string; status?: ProjectStatus; search?: string },
  ): Promise<PaginatedResult<Project>> {
    const { page = 1, limit = 20, sortBy = 'created_at', sortOrder = 'DESC', brandId, status, search } = pagination;

    const queryBuilder = this.projectRepository
      .createQueryBuilder('project')
      .leftJoinAndSelect('project.brand', 'brand')
      .leftJoinAndSelect('project.owner', 'owner')
      .where('project.tenant_id = :tenantId', { tenantId })
      .andWhere('project.deleted_at IS NULL');

    if (brandId) {
      queryBuilder.andWhere('project.brand_id = :brandId', { brandId });
    }

    if (status) {
      queryBuilder.andWhere('project.status = :status', { status });
    }

    if (search) {
      queryBuilder.andWhere(
        '(project.name ILIKE :search OR project.description ILIKE :search)',
        { search: `%${search}%` },
      );
    }

    queryBuilder.orderBy(`project.${sortBy}`, sortOrder);

    const total = await queryBuilder.getCount();
    const totalPages = Math.ceil(total / limit);

    queryBuilder.skip((page - 1) * limit).take(limit);

    const data = await queryBuilder.getMany();

    return {
      data,
      meta: {
        total,
        page,
        limit,
        totalPages,
        hasNextPage: page < totalPages,
        hasPreviousPage: page > 1,
      },
    };
  }

  async findById(tenantId: string, id: string): Promise<Project> {
    const project = await this.projectRepository.findOne({
      where: { id, tenant_id: tenantId, deleted_at: IsNull() },
      relations: ['brand', 'owner'],
    });

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

    return project;
  }

  async findByBrand(tenantId: string, brandId: string): Promise<Project[]> {
    return this.projectRepository.find({
      where: { tenant_id: tenantId, brand_id: brandId, deleted_at: IsNull() },
      relations: ['owner'],
      order: { created_at: 'DESC' },
    });
  }

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

    const project = this.projectRepository.create({
      ...dto,
      slug,
      tenant_id: tenantId,
    });

    return this.projectRepository.save(project);
  }

  async update(tenantId: string, id: string, dto: UpdateProjectDto): Promise<Project> {
    const project = await this.findById(tenantId, id);

    if (dto.slug && dto.slug !== project.slug) {
      // Note: slug uniqueness check could be added here if needed
    }

    Object.assign(project, dto);

    return this.projectRepository.save(project);
  }

  async updateStatus(tenantId: string, id: string, status: ProjectStatus): Promise<Project> {
    const project = await this.findById(tenantId, id);
    project.status = status;

    if (status === ProjectStatus.COMPLETED) {
      project.completed_at = new Date();
      project.progress = 100;
    }

    return this.projectRepository.save(project);
  }

  async updateProgress(tenantId: string, id: string, progress: number): Promise<Project> {
    const project = await this.findById(tenantId, id);
    project.progress = Math.min(100, Math.max(0, progress));
    return this.projectRepository.save(project);
  }

  async softDelete(tenantId: string, id: string): Promise<void> {
    const project = await this.findById(tenantId, id);
    project.deleted_at = new Date();
    await this.projectRepository.save(project);
  }

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