/**
 * IndicadorService - Servicio para indicadores HSE
 *
 * RF-MAA017-008: Gestión de indicadores y reportes HSE
 *
 * @module HSE
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { IndicadorConfig, TipoIndicador, FrecuenciaCalculo } from '../entities/indicador-config.entity';
import { IndicadorMetaObra } from '../entities/indicador-meta-obra.entity';
import { IndicadorValor } from '../entities/indicador-valor.entity';
import { HorasTrabajadas } from '../entities/horas-trabajadas.entity';
import { DiasSinAccidente } from '../entities/dias-sin-accidente.entity';
import { ReporteProgramado } from '../entities/reporte-programado.entity';
import { AlertaIndicador } from '../entities/alerta-indicador.entity';
import { ServiceContext, PaginatedResult } from '../../../shared/services/base.service';

export interface IndicadorFilters {
  tipo?: TipoIndicador;
  activo?: boolean;
  search?: string;
}

export interface CreateIndicadorDto {
  codigo: string;
  nombre: string;
  descripcion?: string;
  tipo: TipoIndicador;
  formula?: string;
  unidad?: string;
  metaGlobal?: number;
  umbralVerde?: number;
  umbralAmarillo?: number;
  umbralRojo?: number;
  frecuenciaCalculo?: FrecuenciaCalculo;
}

export interface IndicadorStats {
  totalIndicadores: number;
  indicadoresActivos: number;
  valoresRegistrados: number;
  alertasActivas: number;
}

export class IndicadorService {
  constructor(
    private readonly indicadorRepository: Repository<IndicadorConfig>,
    private readonly metaRepository: Repository<IndicadorMetaObra>,
    private readonly valorRepository: Repository<IndicadorValor>,
    private readonly horasRepository: Repository<HorasTrabajadas>,
    private readonly diasRepository: Repository<DiasSinAccidente>,
    private readonly reporteRepository: Repository<ReporteProgramado>,
    private readonly alertaRepository: Repository<AlertaIndicador>
  ) {}

  // ========== Configuración de Indicadores ==========
  async findIndicadores(
    ctx: ServiceContext,
    filters: IndicadorFilters = {},
    page: number = 1,
    limit: number = 20
  ): Promise<PaginatedResult<IndicadorConfig>> {
    const skip = (page - 1) * limit;

    const queryBuilder = this.indicadorRepository
      .createQueryBuilder('i')
      .where('i.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (filters.tipo) {
      queryBuilder.andWhere('i.tipo = :tipo', { tipo: filters.tipo });
    }

    if (filters.activo !== undefined) {
      queryBuilder.andWhere('i.activo = :activo', { activo: filters.activo });
    }

    if (filters.search) {
      queryBuilder.andWhere(
        '(i.codigo ILIKE :search OR i.nombre ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

    queryBuilder
      .orderBy('i.nombre', 'ASC')
      .skip(skip)
      .take(limit);

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

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

  async findIndicadorById(ctx: ServiceContext, id: string): Promise<IndicadorConfig | null> {
    return this.indicadorRepository.findOne({
      where: { id, tenantId: ctx.tenantId } as FindOptionsWhere<IndicadorConfig>,
    });
  }

  async createIndicador(ctx: ServiceContext, dto: CreateIndicadorDto): Promise<IndicadorConfig> {
    const indicador = this.indicadorRepository.create({
      tenantId: ctx.tenantId,
      codigo: dto.codigo,
      nombre: dto.nombre,
      descripcion: dto.descripcion,
      tipo: dto.tipo,
      formula: dto.formula,
      unidad: dto.unidad,
      metaGlobal: dto.metaGlobal,
      umbralVerde: dto.umbralVerde,
      umbralAmarillo: dto.umbralAmarillo,
      umbralRojo: dto.umbralRojo,
      frecuenciaCalculo: dto.frecuenciaCalculo || 'mensual',
      activo: true,
    });
    return this.indicadorRepository.save(indicador);
  }

  async updateIndicador(ctx: ServiceContext, id: string, dto: Partial<CreateIndicadorDto>): Promise<IndicadorConfig | null> {
    const indicador = await this.findIndicadorById(ctx, id);
    if (!indicador) return null;

    Object.assign(indicador, dto);
    return this.indicadorRepository.save(indicador);
  }

  async toggleIndicadorActivo(ctx: ServiceContext, id: string): Promise<IndicadorConfig | null> {
    const indicador = await this.findIndicadorById(ctx, id);
    if (!indicador) return null;

    indicador.activo = !indicador.activo;
    return this.indicadorRepository.save(indicador);
  }

  // ========== Metas por Obra ==========
  async findMetas(ctx: ServiceContext, fraccionamientoId?: string, indicadorId?: string): Promise<IndicadorMetaObra[]> {
    const queryBuilder = this.metaRepository
      .createQueryBuilder('m')
      .leftJoinAndSelect('m.fraccionamiento', 'fraccionamiento')
      .leftJoinAndSelect('m.indicador', 'indicador')
      .innerJoin('m.indicador', 'ind')
      .where('ind.tenant_id = :tenantId', { tenantId: ctx.tenantId });

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

    if (indicadorId) {
      queryBuilder.andWhere('m.indicador_id = :indicadorId', { indicadorId });
    }

    return queryBuilder.getMany();
  }

  // ========== Valores de Indicadores ==========
  async findValores(
    ctx: ServiceContext,
    indicadorId: string,
    fraccionamientoId?: string,
    page: number = 1,
    limit: number = 50
  ): Promise<PaginatedResult<IndicadorValor>> {
    const skip = (page - 1) * limit;

    const queryBuilder = this.valorRepository
      .createQueryBuilder('v')
      .leftJoinAndSelect('v.fraccionamiento', 'fraccionamiento')
      .leftJoinAndSelect('v.indicador', 'indicador')
      .innerJoin('v.indicador', 'ind')
      .where('ind.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('v.indicador_id = :indicadorId', { indicadorId });

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

    queryBuilder
      .orderBy('v.created_at', 'DESC')
      .skip(skip)
      .take(limit);

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

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

  // ========== Horas Trabajadas ==========
  async findHorasTrabajadas(ctx: ServiceContext, fraccionamientoId: string, year?: number): Promise<HorasTrabajadas[]> {
    const queryBuilder = this.horasRepository
      .createQueryBuilder('h')
      .where('h.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('h.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId });

    if (year) {
      queryBuilder.andWhere('EXTRACT(YEAR FROM h.fecha) = :year', { year });
    }

    return queryBuilder.orderBy('h.fecha', 'DESC').getMany();
  }

  // ========== Días Sin Accidente ==========
  async findDiasSinAccidente(ctx: ServiceContext, fraccionamientoId: string): Promise<DiasSinAccidente | null> {
    return this.diasRepository.findOne({
      where: { tenantId: ctx.tenantId, fraccionamientoId } as FindOptionsWhere<DiasSinAccidente>,
    });
  }

  // ========== Reportes Programados ==========
  async findReportes(ctx: ServiceContext): Promise<ReporteProgramado[]> {
    return this.reporteRepository.find({
      where: { tenantId: ctx.tenantId } as FindOptionsWhere<ReporteProgramado>,
      order: { createdAt: 'DESC' },
    });
  }

  // ========== Alertas ==========
  async findAlertas(
    ctx: ServiceContext,
    fraccionamientoId?: string,
    page: number = 1,
    limit: number = 20
  ): Promise<PaginatedResult<AlertaIndicador>> {
    const skip = (page - 1) * limit;

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

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

    queryBuilder
      .orderBy('a.created_at', 'DESC')
      .skip(skip)
      .take(limit);

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

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

  // ========== Estadísticas ==========
  async getStats(ctx: ServiceContext): Promise<IndicadorStats> {
    const totalIndicadores = await this.indicadorRepository.count({
      where: { tenantId: ctx.tenantId } as FindOptionsWhere<IndicadorConfig>,
    });

    const indicadoresActivos = await this.indicadorRepository.count({
      where: { tenantId: ctx.tenantId, activo: true } as FindOptionsWhere<IndicadorConfig>,
    });

    const valoresRegistrados = await this.valorRepository
      .createQueryBuilder('v')
      .innerJoin('v.indicador', 'i')
      .where('i.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .getCount();

    const alertasActivas = await this.alertaRepository.count({
      where: { tenantId: ctx.tenantId } as FindOptionsWhere<AlertaIndicador>,
    });

    return {
      totalIndicadores,
      indicadoresActivos,
      valoresRegistrados,
      alertasActivas,
    };
  }
}
