/**
 * DiasSinAccidenteService - Servicio para contador de dias sin accidente
 *
 * RF-MAA017-008: Gestion de indicadores de dias sin accidente por obra
 *
 * @module HSE
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { DiasSinAccidente } from '../entities/dias-sin-accidente.entity';
import { Incidente, TipoIncidente } from '../entities/incidente.entity';
import { ServiceContext } from '../../../shared/services/base.service';

export interface CreateDiasSinAccidenteDto {
  fraccionamientoId: string;
  fechaInicioConteo: Date;
}

export interface DiasSinAccidenteStats {
  fraccionamientoId: string;
  diasAcumulados: number;
  recordHistorico: number;
  fechaInicioConteo: Date;
  ultimoAccidenteFecha?: Date;
}

export interface DiasSinAccidenteResumen {
  totalObras: number;
  obraConMasDias: { fraccionamientoId: string; dias: number } | null;
  obraConMenosDias: { fraccionamientoId: string; dias: number } | null;
  promedioDias: number;
}

export class DiasSinAccidenteService {
  constructor(
    private readonly diasSinAccidenteRepository: Repository<DiasSinAccidente>,
    private readonly incidenteRepository: Repository<Incidente>
  ) {}

  /**
   * Obtiene el contador de dias sin accidente para una obra
   */
  async findByFraccionamiento(
    ctx: ServiceContext,
    fraccionamientoId: string
  ): Promise<DiasSinAccidente | null> {
    return this.diasSinAccidenteRepository.findOne({
      where: {
        tenantId: ctx.tenantId,
        fraccionamientoId,
      } as FindOptionsWhere<DiasSinAccidente>,
      relations: ['fraccionamiento', 'ultimoIncidente'],
    });
  }

  /**
   * Lista todos los contadores de dias sin accidente del tenant
   */
  async findAll(ctx: ServiceContext): Promise<DiasSinAccidente[]> {
    return this.diasSinAccidenteRepository.find({
      where: { tenantId: ctx.tenantId } as FindOptionsWhere<DiasSinAccidente>,
      relations: ['fraccionamiento'],
      order: { diasAcumulados: 'DESC' },
    });
  }

  /**
   * Inicializa el contador para una obra
   */
  async initialize(
    ctx: ServiceContext,
    dto: CreateDiasSinAccidenteDto
  ): Promise<DiasSinAccidente> {
    // Verificar si ya existe
    const existing = await this.findByFraccionamiento(ctx, dto.fraccionamientoId);
    if (existing) {
      throw new Error('El contador ya existe para esta obra');
    }

    const contador = this.diasSinAccidenteRepository.create({
      tenantId: ctx.tenantId,
      fraccionamientoId: dto.fraccionamientoId,
      fechaInicioConteo: dto.fechaInicioConteo,
      diasAcumulados: this.calculateDays(dto.fechaInicioConteo),
      recordHistorico: 0,
    });

    return this.diasSinAccidenteRepository.save(contador);
  }

  /**
   * Reinicia el contador por un nuevo accidente
   */
  async resetByAccident(
    ctx: ServiceContext,
    fraccionamientoId: string,
    incidenteId: string
  ): Promise<DiasSinAccidente | null> {
    const contador = await this.findByFraccionamiento(ctx, fraccionamientoId);
    if (!contador) {
      return null;
    }

    // Actualizar record si es necesario
    const diasActuales = this.calculateDays(contador.fechaInicioConteo);
    if (diasActuales > contador.recordHistorico) {
      contador.recordHistorico = diasActuales;
    }

    // Reiniciar conteo
    contador.fechaInicioConteo = new Date();
    contador.diasAcumulados = 0;
    contador.ultimoIncidenteId = incidenteId;

    return this.diasSinAccidenteRepository.save(contador);
  }

  /**
   * Actualiza los dias acumulados (llamado por cron o manualmente)
   */
  async updateDiasAcumulados(
    ctx: ServiceContext,
    fraccionamientoId: string
  ): Promise<DiasSinAccidente | null> {
    const contador = await this.findByFraccionamiento(ctx, fraccionamientoId);
    if (!contador) {
      return null;
    }

    contador.diasAcumulados = this.calculateDays(contador.fechaInicioConteo);

    // Actualizar record si supera el historico
    if (contador.diasAcumulados > contador.recordHistorico) {
      contador.recordHistorico = contador.diasAcumulados;
    }

    return this.diasSinAccidenteRepository.save(contador);
  }

  /**
   * Actualiza todos los contadores del tenant
   */
  async updateAllContadores(ctx: ServiceContext): Promise<number> {
    const contadores = await this.findAll(ctx);
    let updated = 0;

    for (const contador of contadores) {
      const dias = this.calculateDays(contador.fechaInicioConteo);
      if (dias !== contador.diasAcumulados) {
        contador.diasAcumulados = dias;
        if (dias > contador.recordHistorico) {
          contador.recordHistorico = dias;
        }
        await this.diasSinAccidenteRepository.save(contador);
        updated++;
      }
    }

    return updated;
  }

  /**
   * Obtiene estadisticas de una obra
   */
  async getStats(
    ctx: ServiceContext,
    fraccionamientoId: string
  ): Promise<DiasSinAccidenteStats | null> {
    const contador = await this.findByFraccionamiento(ctx, fraccionamientoId);
    if (!contador) {
      return null;
    }

    // Obtener ultimo accidente
    const ultimoAccidente = await this.incidenteRepository.findOne({
      where: {
        tenantId: ctx.tenantId,
        fraccionamientoId,
        tipo: 'accidente' as TipoIncidente,
      } as FindOptionsWhere<Incidente>,
      order: { fechaHora: 'DESC' },
    });

    return {
      fraccionamientoId,
      diasAcumulados: this.calculateDays(contador.fechaInicioConteo),
      recordHistorico: contador.recordHistorico,
      fechaInicioConteo: contador.fechaInicioConteo,
      ultimoAccidenteFecha: ultimoAccidente?.fechaHora,
    };
  }

  /**
   * Obtiene resumen de dias sin accidente del tenant
   */
  async getResumen(ctx: ServiceContext): Promise<DiasSinAccidenteResumen> {
    const contadores = await this.findAll(ctx);

    if (contadores.length === 0) {
      return {
        totalObras: 0,
        obraConMasDias: null,
        obraConMenosDias: null,
        promedioDias: 0,
      };
    }

    // Actualizar dias acumulados
    const contadoresActualizados = contadores.map((c) => ({
      ...c,
      diasAcumulados: this.calculateDays(c.fechaInicioConteo),
    }));

    const sorted = contadoresActualizados.sort((a, b) => b.diasAcumulados - a.diasAcumulados);
    const totalDias = sorted.reduce((sum, c) => sum + c.diasAcumulados, 0);

    return {
      totalObras: contadores.length,
      obraConMasDias: {
        fraccionamientoId: sorted[0].fraccionamientoId,
        dias: sorted[0].diasAcumulados,
      },
      obraConMenosDias: {
        fraccionamientoId: sorted[sorted.length - 1].fraccionamientoId,
        dias: sorted[sorted.length - 1].diasAcumulados,
      },
      promedioDias: Math.round(totalDias / contadores.length),
    };
  }

  /**
   * Verifica si se debe reiniciar el contador basado en incidentes recientes
   */
  async checkAndResetIfNeeded(
    ctx: ServiceContext,
    fraccionamientoId: string
  ): Promise<{ reset: boolean; incidenteId?: string }> {
    const contador = await this.findByFraccionamiento(ctx, fraccionamientoId);
    if (!contador) {
      return { reset: false };
    }

    // Buscar accidentes despues del inicio del conteo
    const accidenteReciente = await this.incidenteRepository.findOne({
      where: {
        tenantId: ctx.tenantId,
        fraccionamientoId,
        tipo: 'accidente' as TipoIncidente,
      } as FindOptionsWhere<Incidente>,
      order: { fechaHora: 'DESC' },
    });

    if (
      accidenteReciente &&
      new Date(accidenteReciente.fechaHora) > new Date(contador.fechaInicioConteo) &&
      accidenteReciente.id !== contador.ultimoIncidenteId
    ) {
      await this.resetByAccident(ctx, fraccionamientoId, accidenteReciente.id);
      return { reset: true, incidenteId: accidenteReciente.id };
    }

    return { reset: false };
  }

  /**
   * Calcula dias desde una fecha hasta hoy
   */
  private calculateDays(fechaInicio: Date): number {
    const inicio = new Date(fechaInicio);
    inicio.setHours(0, 0, 0, 0);
    const hoy = new Date();
    hoy.setHours(0, 0, 0, 0);
    const diff = hoy.getTime() - inicio.getTime();
    return Math.max(0, Math.floor(diff / (1000 * 60 * 60 * 24)));
  }
}
