/**
 * ConstanciaDc3Service - Servicio para gestión de constancias DC-3
 *
 * RF-MAA017-002: Control de Capacitaciones - Constancias DC-3 STPS
 *
 * @module HSE
 */

import { Repository, FindOptionsWhere, LessThan } from 'typeorm';
import { ConstanciaDc3 } from '../entities/constancia-dc3.entity';
import { ServiceContext, PaginatedResult } from '../../../shared/services/base.service';

export interface CreateConstanciaDc3Dto {
  folio: string;
  asistenteId: string;
  employeeId: string;
  capacitacionId: string;
  fechaEmision: Date;
  fechaVencimiento?: Date;
  documentoUrl?: string;
}

export interface ConstanciaDc3Filters {
  employeeId?: string;
  capacitacionId?: string;
  vigente?: boolean;
  vencidaProximamente?: boolean;
  diasProximoVencimiento?: number;
  dateFrom?: Date;
  dateTo?: Date;
  search?: string;
}

export interface ConstanciaDc3Stats {
  totalConstancias: number;
  vigentes: number;
  vencidas: number;
  porVencer: number;
  sinVencimiento: number;
}

export interface EmpleadoCapacitacionStatus {
  employeeId: string;
  employeeName: string;
  capacitacionId: string;
  capacitacionNombre: string;
  constanciaId?: string;
  folio?: string;
  fechaEmision?: Date;
  fechaVencimiento?: Date;
  estado: 'vigente' | 'vencida' | 'por_vencer' | 'sin_constancia';
}

export class ConstanciaDc3Service {
  constructor(private readonly repository: Repository<ConstanciaDc3>) {}

  async findAll(
    ctx: ServiceContext,
    filters: ConstanciaDc3Filters = {},
    page: number = 1,
    limit: number = 20
  ): Promise<PaginatedResult<ConstanciaDc3>> {
    const skip = (page - 1) * limit;
    const today = new Date();

    const queryBuilder = this.repository
      .createQueryBuilder('constancia')
      .leftJoinAndSelect('constancia.employee', 'employee')
      .leftJoinAndSelect('constancia.capacitacion', 'capacitacion')
      .leftJoinAndSelect('constancia.asistente', 'asistente')
      .where('constancia.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (filters.employeeId) {
      queryBuilder.andWhere('constancia.employee_id = :employeeId', {
        employeeId: filters.employeeId,
      });
    }

    if (filters.capacitacionId) {
      queryBuilder.andWhere('constancia.capacitacion_id = :capacitacionId', {
        capacitacionId: filters.capacitacionId,
      });
    }

    if (filters.vigente === true) {
      queryBuilder.andWhere(
        '(constancia.fecha_vencimiento IS NULL OR constancia.fecha_vencimiento >= :today)',
        { today }
      );
    }

    if (filters.vigente === false) {
      queryBuilder.andWhere('constancia.fecha_vencimiento < :today', { today });
    }

    if (filters.vencidaProximamente) {
      const diasLimite = filters.diasProximoVencimiento || 30;
      const fechaLimite = new Date();
      fechaLimite.setDate(fechaLimite.getDate() + diasLimite);
      queryBuilder.andWhere(
        'constancia.fecha_vencimiento >= :today AND constancia.fecha_vencimiento <= :fechaLimite',
        { today, fechaLimite }
      );
    }

    if (filters.dateFrom) {
      queryBuilder.andWhere('constancia.fecha_emision >= :dateFrom', { dateFrom: filters.dateFrom });
    }

    if (filters.dateTo) {
      queryBuilder.andWhere('constancia.fecha_emision <= :dateTo', { dateTo: filters.dateTo });
    }

    if (filters.search) {
      queryBuilder.andWhere(
        '(constancia.folio ILIKE :search OR employee.nombres ILIKE :search OR employee.apellido_paterno ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

    queryBuilder
      .orderBy('constancia.fecha_emision', 'DESC')
      .skip(skip)
      .take(limit);

    const [data, total] = await queryBuilder.getManyAndCount();

    return {
      data,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async findById(ctx: ServiceContext, id: string): Promise<ConstanciaDc3 | null> {
    return this.repository.findOne({
      where: { id, tenantId: ctx.tenantId } as FindOptionsWhere<ConstanciaDc3>,
      relations: ['employee', 'capacitacion', 'asistente'],
    });
  }

  async findByFolio(ctx: ServiceContext, folio: string): Promise<ConstanciaDc3 | null> {
    return this.repository.findOne({
      where: { tenantId: ctx.tenantId, folio } as FindOptionsWhere<ConstanciaDc3>,
      relations: ['employee', 'capacitacion'],
    });
  }

  async create(ctx: ServiceContext, dto: CreateConstanciaDc3Dto): Promise<ConstanciaDc3> {
    const existingFolio = await this.findByFolio(ctx, dto.folio);
    if (existingFolio) {
      throw new Error(`Ya existe una constancia DC-3 con el folio ${dto.folio}`);
    }

    const constancia = this.repository.create({
      tenantId: ctx.tenantId,
      folio: dto.folio,
      asistenteId: dto.asistenteId,
      employeeId: dto.employeeId,
      capacitacionId: dto.capacitacionId,
      fechaEmision: dto.fechaEmision,
      fechaVencimiento: dto.fechaVencimiento,
      documentoUrl: dto.documentoUrl,
    });

    return this.repository.save(constancia);
  }

  async updateDocumentoUrl(ctx: ServiceContext, id: string, documentoUrl: string): Promise<ConstanciaDc3 | null> {
    const constancia = await this.findById(ctx, id);
    if (!constancia) return null;

    constancia.documentoUrl = documentoUrl;
    return this.repository.save(constancia);
  }

  async findByEmployee(ctx: ServiceContext, employeeId: string): Promise<ConstanciaDc3[]> {
    return this.repository.find({
      where: { tenantId: ctx.tenantId, employeeId } as FindOptionsWhere<ConstanciaDc3>,
      relations: ['capacitacion'],
      order: { fechaEmision: 'DESC' },
    });
  }

  async findVigenteByEmployeeAndCapacitacion(
    ctx: ServiceContext,
    employeeId: string,
    capacitacionId: string
  ): Promise<ConstanciaDc3 | null> {
    const today = new Date();

    return this.repository
      .createQueryBuilder('c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('c.employee_id = :employeeId', { employeeId })
      .andWhere('c.capacitacion_id = :capacitacionId', { capacitacionId })
      .andWhere('(c.fecha_vencimiento IS NULL OR c.fecha_vencimiento >= :today)', { today })
      .orderBy('c.fecha_emision', 'DESC')
      .getOne();
  }

  async getConstanciasVencidas(ctx: ServiceContext): Promise<ConstanciaDc3[]> {
    const today = new Date();

    return this.repository.find({
      where: {
        tenantId: ctx.tenantId,
        fechaVencimiento: LessThan(today),
      } as FindOptionsWhere<ConstanciaDc3>,
      relations: ['employee', 'capacitacion'],
      order: { fechaVencimiento: 'ASC' },
    });
  }

  async getConstanciasPorVencer(ctx: ServiceContext, dias: number = 30): Promise<ConstanciaDc3[]> {
    const today = new Date();
    const fechaLimite = new Date();
    fechaLimite.setDate(fechaLimite.getDate() + dias);

    return this.repository
      .createQueryBuilder('c')
      .leftJoinAndSelect('c.employee', 'employee')
      .leftJoinAndSelect('c.capacitacion', 'capacitacion')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('c.fecha_vencimiento >= :today', { today })
      .andWhere('c.fecha_vencimiento <= :fechaLimite', { fechaLimite })
      .orderBy('c.fecha_vencimiento', 'ASC')
      .getMany();
  }

  async getStats(ctx: ServiceContext): Promise<ConstanciaDc3Stats> {
    const today = new Date();
    const treintaDias = new Date();
    treintaDias.setDate(treintaDias.getDate() + 30);

    const totalConstancias = await this.repository.count({
      where: { tenantId: ctx.tenantId } as FindOptionsWhere<ConstanciaDc3>,
    });

    const vigentes = await this.repository
      .createQueryBuilder('c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('(c.fecha_vencimiento IS NULL OR c.fecha_vencimiento >= :today)', { today })
      .getCount();

    const vencidas = await this.repository.count({
      where: {
        tenantId: ctx.tenantId,
        fechaVencimiento: LessThan(today),
      } as FindOptionsWhere<ConstanciaDc3>,
    });

    const porVencer = await this.repository
      .createQueryBuilder('c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('c.fecha_vencimiento >= :today', { today })
      .andWhere('c.fecha_vencimiento <= :treintaDias', { treintaDias })
      .getCount();

    const sinVencimiento = await this.repository
      .createQueryBuilder('c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('c.fecha_vencimiento IS NULL')
      .getCount();

    return {
      totalConstancias,
      vigentes,
      vencidas,
      porVencer,
      sinVencimiento,
    };
  }

  async generateFolio(ctx: ServiceContext, year?: number): Promise<string> {
    const anio = year || new Date().getFullYear();
    const prefix = `DC3-${anio}`;

    const lastConstancia = await this.repository
      .createQueryBuilder('c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('c.folio LIKE :prefix', { prefix: `${prefix}%` })
      .orderBy('c.folio', 'DESC')
      .getOne();

    let nextNumber = 1;
    if (lastConstancia) {
      const parts = lastConstancia.folio.split('-');
      if (parts.length >= 3) {
        nextNumber = parseInt(parts[2], 10) + 1;
      }
    }

    return `${prefix}-${nextNumber.toString().padStart(5, '0')}`;
  }
}
