/**
 * ComisionSeguridadService - Servicio para comisiones de seguridad e higiene
 *
 * RF-MAA017-005: Cumplimiento STPS - Comisiones de Seguridad
 *
 * @module HSE
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { ComisionSeguridad, EstadoComision } from '../entities/comision-seguridad.entity';
import { ComisionIntegrante, RolComision, Representacion } from '../entities/comision-integrante.entity';
import { ComisionRecorrido } from '../entities/comision-recorrido.entity';
import { ServiceContext, PaginatedResult } from '../../../shared/services/base.service';

export interface CreateComisionSeguridadDto {
  fraccionamientoId: string;
  fechaConstitucion: Date;
  numeroActa?: string;
  vigenciaInicio: Date;
  vigenciaFin: Date;
  documentoActaUrl?: string;
}

export interface UpdateComisionSeguridadDto {
  numeroActa?: string;
  vigenciaInicio?: Date;
  vigenciaFin?: Date;
  documentoActaUrl?: string;
}

export interface ComisionSeguridadFilters {
  fraccionamientoId?: string;
  estado?: EstadoComision;
  vigente?: boolean;
}

export interface AddIntegranteComisionDto {
  employeeId: string;
  rol: RolComision;
  representacion: Representacion;
  fechaNombramiento: Date;
}

export interface CreateRecorridoComisionDto {
  comisionId: string;
  fechaProgramada: Date;
  areasRecorridas?: string;
}

export interface CompletarRecorridoDto {
  fechaRealizada?: Date;
  hallazgos?: string;
  recomendaciones?: string;
  numeroActa?: string;
  documentoActaUrl?: string;
}

export interface ComisionStats {
  totalComisiones: number;
  comisionesActivas: number;
  comisionesVencidas: number;
  totalIntegrantes: number;
  recorridosProgramados: number;
  recorridosRealizados: number;
  recorridosPendientes: number;
}

export type EstadoRecorrido = 'programado' | 'realizado' | 'cancelado' | 'pendiente';

export class ComisionSeguridadService {
  constructor(
    private readonly comisionRepository: Repository<ComisionSeguridad>,
    private readonly integranteRepository: Repository<ComisionIntegrante>,
    private readonly recorridoRepository: Repository<ComisionRecorrido>
  ) {}

  // ========== Comisiones ==========
  async findComisiones(
    ctx: ServiceContext,
    filters: ComisionSeguridadFilters = {},
    page: number = 1,
    limit: number = 20
  ): Promise<PaginatedResult<ComisionSeguridad>> {
    const skip = (page - 1) * limit;
    const today = new Date();

    const queryBuilder = this.comisionRepository
      .createQueryBuilder('c')
      .leftJoinAndSelect('c.fraccionamiento', 'fraccionamiento')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (filters.fraccionamientoId) {
      queryBuilder.andWhere('c.fraccionamiento_id = :fraccionamientoId', {
        fraccionamientoId: filters.fraccionamientoId,
      });
    }

    if (filters.estado) {
      queryBuilder.andWhere('c.estado = :estado', { estado: filters.estado });
    }

    if (filters.vigente === true) {
      queryBuilder.andWhere('c.vigencia_fin >= :today', { today });
      queryBuilder.andWhere('c.estado = :estado', { estado: 'activa' });
    }

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

    queryBuilder
      .orderBy('c.fecha_constitucion', '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<ComisionSeguridad | null> {
    return this.comisionRepository.findOne({
      where: { id, tenantId: ctx.tenantId } as FindOptionsWhere<ComisionSeguridad>,
      relations: ['fraccionamiento', 'integrantes', 'integrantes.employee', 'recorridos'],
    });
  }

  async findVigenteByFraccionamiento(ctx: ServiceContext, fraccionamientoId: string): Promise<ComisionSeguridad | null> {
    const today = new Date();

    return this.comisionRepository
      .createQueryBuilder('c')
      .leftJoinAndSelect('c.integrantes', 'integrantes', 'integrantes.activo = true')
      .leftJoinAndSelect('integrantes.employee', 'employee')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('c.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId })
      .andWhere('c.estado = :estado', { estado: 'activa' })
      .andWhere('c.vigencia_fin >= :today', { today })
      .getOne();
  }

  async create(ctx: ServiceContext, dto: CreateComisionSeguridadDto): Promise<ComisionSeguridad> {
    const existingActive = await this.findVigenteByFraccionamiento(ctx, dto.fraccionamientoId);
    if (existingActive) {
      throw new Error('Ya existe una comisión activa para este fraccionamiento');
    }

    const comision = this.comisionRepository.create({
      tenantId: ctx.tenantId,
      fraccionamientoId: dto.fraccionamientoId,
      fechaConstitucion: dto.fechaConstitucion,
      numeroActa: dto.numeroActa,
      vigenciaInicio: dto.vigenciaInicio,
      vigenciaFin: dto.vigenciaFin,
      documentoActaUrl: dto.documentoActaUrl,
      estado: 'activa',
    });

    return this.comisionRepository.save(comision);
  }

  async update(ctx: ServiceContext, id: string, dto: UpdateComisionSeguridadDto): Promise<ComisionSeguridad | null> {
    const comision = await this.findById(ctx, id);
    if (!comision) return null;

    if (dto.numeroActa !== undefined) comision.numeroActa = dto.numeroActa;
    if (dto.vigenciaInicio) comision.vigenciaInicio = dto.vigenciaInicio;
    if (dto.vigenciaFin) comision.vigenciaFin = dto.vigenciaFin;
    if (dto.documentoActaUrl !== undefined) comision.documentoActaUrl = dto.documentoActaUrl;

    return this.comisionRepository.save(comision);
  }

  async updateEstado(ctx: ServiceContext, id: string, estado: EstadoComision): Promise<ComisionSeguridad | null> {
    const comision = await this.findById(ctx, id);
    if (!comision) return null;

    comision.estado = estado;
    return this.comisionRepository.save(comision);
  }

  async renovarComision(
    ctx: ServiceContext,
    comisionAnteriorId: string,
    dto: CreateComisionSeguridadDto
  ): Promise<ComisionSeguridad> {
    const comisionAnterior = await this.findById(ctx, comisionAnteriorId);
    if (!comisionAnterior) {
      throw new Error('Comisión anterior no encontrada');
    }

    comisionAnterior.estado = 'renovada';
    await this.comisionRepository.save(comisionAnterior);

    return this.create(ctx, dto);
  }

  // ========== Integrantes ==========
  async findIntegrantesByComision(comisionId: string, soloActivos: boolean = true): Promise<ComisionIntegrante[]> {
    const where: FindOptionsWhere<ComisionIntegrante> = { comisionId };
    if (soloActivos) {
      where.activo = true;
    }

    return this.integranteRepository.find({
      where,
      relations: ['employee'],
      order: { rol: 'ASC', representacion: 'ASC' },
    });
  }

  async addIntegrante(comisionId: string, dto: AddIntegranteComisionDto): Promise<ComisionIntegrante> {
    const existing = await this.integranteRepository.findOne({
      where: {
        comisionId,
        employeeId: dto.employeeId,
        activo: true,
      } as FindOptionsWhere<ComisionIntegrante>,
    });

    if (existing) {
      throw new Error('El empleado ya es integrante activo de esta comisión');
    }

    const integrante = this.integranteRepository.create({
      comisionId,
      employeeId: dto.employeeId,
      rol: dto.rol,
      representacion: dto.representacion,
      fechaNombramiento: dto.fechaNombramiento,
      activo: true,
    });

    return this.integranteRepository.save(integrante);
  }

  async updateIntegrante(
    integranteId: string,
    rol?: RolComision,
    representacion?: Representacion
  ): Promise<ComisionIntegrante | null> {
    const integrante = await this.integranteRepository.findOne({
      where: { id: integranteId } as FindOptionsWhere<ComisionIntegrante>,
    });

    if (!integrante) return null;

    if (rol) integrante.rol = rol;
    if (representacion) integrante.representacion = representacion;

    return this.integranteRepository.save(integrante);
  }

  async bajaIntegrante(integranteId: string): Promise<ComisionIntegrante | null> {
    const integrante = await this.integranteRepository.findOne({
      where: { id: integranteId } as FindOptionsWhere<ComisionIntegrante>,
    });

    if (!integrante) return null;

    integrante.activo = false;

    return this.integranteRepository.save(integrante);
  }

  // ========== Recorridos ==========
  async findRecorridosByComision(comisionId: string): Promise<ComisionRecorrido[]> {
    return this.recorridoRepository.find({
      where: { comisionId } as FindOptionsWhere<ComisionRecorrido>,
      order: { fechaProgramada: 'DESC' },
    });
  }

  async findRecorridoById(id: string): Promise<ComisionRecorrido | null> {
    return this.recorridoRepository.findOne({
      where: { id } as FindOptionsWhere<ComisionRecorrido>,
      relations: ['comision', 'comision.fraccionamiento'],
    });
  }

  async createRecorrido(dto: CreateRecorridoComisionDto): Promise<ComisionRecorrido> {
    const recorrido = this.recorridoRepository.create({
      comisionId: dto.comisionId,
      fechaProgramada: dto.fechaProgramada,
      areasRecorridas: dto.areasRecorridas,
      estado: 'programado',
    });

    return this.recorridoRepository.save(recorrido);
  }

  async completarRecorrido(id: string, dto: CompletarRecorridoDto): Promise<ComisionRecorrido | null> {
    const recorrido = await this.findRecorridoById(id);
    if (!recorrido) return null;

    recorrido.fechaRealizada = dto.fechaRealizada || new Date();
    if (dto.hallazgos) recorrido.hallazgos = dto.hallazgos;
    if (dto.recomendaciones) recorrido.recomendaciones = dto.recomendaciones;
    if (dto.numeroActa) recorrido.numeroActa = dto.numeroActa;
    if (dto.documentoActaUrl) recorrido.documentoActaUrl = dto.documentoActaUrl;
    recorrido.estado = 'realizado';

    return this.recorridoRepository.save(recorrido);
  }

  async cancelarRecorrido(id: string): Promise<ComisionRecorrido | null> {
    const recorrido = await this.findRecorridoById(id);
    if (!recorrido) return null;

    if (recorrido.estado === 'realizado') {
      throw new Error('No se puede cancelar un recorrido ya realizado');
    }

    recorrido.estado = 'cancelado';
    return this.recorridoRepository.save(recorrido);
  }

  async getRecorridosPendientes(ctx: ServiceContext, fraccionamientoId?: string): Promise<ComisionRecorrido[]> {
    const today = new Date();

    const queryBuilder = this.recorridoRepository
      .createQueryBuilder('r')
      .innerJoinAndSelect('r.comision', 'comision')
      .leftJoinAndSelect('comision.fraccionamiento', 'fraccionamiento')
      .where('comision.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('r.estado IN (:...estados)', { estados: ['programado', 'pendiente'] })
      .andWhere('r.fecha_programada <= :today', { today });

    if (fraccionamientoId) {
      queryBuilder.andWhere('comision.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId });
    }

    return queryBuilder.orderBy('r.fecha_programada', 'ASC').getMany();
  }

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

    return this.recorridoRepository
      .createQueryBuilder('r')
      .innerJoinAndSelect('r.comision', 'comision')
      .leftJoinAndSelect('comision.fraccionamiento', 'fraccionamiento')
      .where('comision.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('r.estado = :estado', { estado: 'programado' })
      .andWhere('r.fecha_programada >= :today', { today })
      .andWhere('r.fecha_programada <= :fechaLimite', { fechaLimite })
      .orderBy('r.fecha_programada', 'ASC')
      .getMany();
  }

  // ========== Verificar Vigencia ==========
  async verificarYActualizarVigencias(ctx: ServiceContext): Promise<number> {
    const today = new Date();

    const comisionesVencidas = await this.comisionRepository
      .createQueryBuilder('c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('c.estado = :estado', { estado: 'activa' })
      .andWhere('c.vigencia_fin < :today', { today })
      .getMany();

    for (const comision of comisionesVencidas) {
      comision.estado = 'vencida';
      await this.comisionRepository.save(comision);
    }

    return comisionesVencidas.length;
  }

  // ========== Estadísticas ==========
  async getStats(ctx: ServiceContext, fraccionamientoId?: string): Promise<ComisionStats> {
    const today = new Date();

    const comisionQuery = this.comisionRepository
      .createQueryBuilder('c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (fraccionamientoId) {
      comisionQuery.andWhere('c.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId });
    }

    const totalComisiones = await comisionQuery.getCount();

    const comisionesActivas = await this.comisionRepository
      .createQueryBuilder('c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('c.estado = :estado', { estado: 'activa' })
      .andWhere('c.vigencia_fin >= :today', { today })
      .andWhere(fraccionamientoId ? 'c.fraccionamiento_id = :fraccionamientoId' : '1=1', { fraccionamientoId })
      .getCount();

    const comisionesVencidas = await this.comisionRepository
      .createQueryBuilder('c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('c.estado = :estado', { estado: 'vencida' })
      .andWhere(fraccionamientoId ? 'c.fraccionamiento_id = :fraccionamientoId' : '1=1', { fraccionamientoId })
      .getCount();

    const totalIntegrantes = await this.integranteRepository
      .createQueryBuilder('i')
      .innerJoin('i.comision', 'c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('i.activo = true')
      .andWhere(fraccionamientoId ? 'c.fraccionamiento_id = :fraccionamientoId' : '1=1', { fraccionamientoId })
      .getCount();

    const recorridosQuery = this.recorridoRepository
      .createQueryBuilder('r')
      .innerJoin('r.comision', 'c')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (fraccionamientoId) {
      recorridosQuery.andWhere('c.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId });
    }

    const recorridosProgramados = await recorridosQuery
      .clone()
      .andWhere('r.estado = :estado', { estado: 'programado' })
      .getCount();

    const recorridosRealizados = await recorridosQuery
      .clone()
      .andWhere('r.estado = :estado', { estado: 'realizado' })
      .getCount();

    const recorridosPendientes = await recorridosQuery
      .clone()
      .andWhere('r.estado IN (:...estados)', { estados: ['programado', 'pendiente'] })
      .andWhere('r.fecha_programada < :today', { today })
      .getCount();

    return {
      totalComisiones,
      comisionesActivas,
      comisionesVencidas,
      totalIntegrantes,
      recorridosProgramados,
      recorridosRealizados,
      recorridosPendientes,
    };
  }
}
