/**
 * InspeccionService - Servicio para inspecciones de seguridad
 *
 * RF-MAA017-003: Gestión de inspecciones, hallazgos y seguimiento
 *
 * @module HSE
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { Inspeccion } from '../entities/inspeccion.entity';
import { InspeccionEvaluacion } from '../entities/inspeccion-evaluacion.entity';
import { Hallazgo, GravedadHallazgo, EstadoHallazgo } from '../entities/hallazgo.entity';
import { HallazgoEvidencia } from '../entities/hallazgo-evidencia.entity';
import { TipoInspeccion } from '../entities/tipo-inspeccion.entity';
import { ServiceContext, PaginatedResult } from '../../../shared/services/base.service';

export interface InspeccionFilters {
  fraccionamientoId?: string;
  tipoInspeccionId?: string;
  estado?: string;
  inspectorId?: string;
  dateFrom?: Date;
  dateTo?: Date;
}

export interface HallazgoFilters {
  fraccionamientoId?: string;
  gravedad?: GravedadHallazgo;
  estado?: EstadoHallazgo;
  responsableId?: string;
  vencidos?: boolean;
}

export interface CreateInspeccionDto {
  tipoInspeccionId: string;
  fraccionamientoId: string;
  inspectorId: string;
  programaId?: string;
}

export interface CreateHallazgoDto {
  evaluacionId?: string;
  gravedad: GravedadHallazgo;
  tipo: 'acto_inseguro' | 'condicion_insegura';
  descripcion: string;
  ubicacionDescripcion?: string;
  responsableCorreccionId?: string;
  fechaLimite: Date;
}

export interface InspeccionStats {
  totalInspecciones: number;
  hallazgosAbiertos: number;
  hallazgosVencidos: number;
}

export class InspeccionService {
  constructor(
    private readonly inspeccionRepository: Repository<Inspeccion>,
    private readonly evaluacionRepository: Repository<InspeccionEvaluacion>,
    private readonly hallazgoRepository: Repository<Hallazgo>,
    private readonly evidenciaRepository: Repository<HallazgoEvidencia>,
    private readonly tipoInspeccionRepository: Repository<TipoInspeccion>
  ) {}

  // Getters para repositorios (para uso futuro)
  getEvaluacionRepository() {
    return this.evaluacionRepository;
  }

  getEvidenciaRepository() {
    return this.evidenciaRepository;
  }

  private generateFolio(prefix: string): string {
    const now = new Date();
    const year = now.getFullYear().toString().slice(-2);
    const month = (now.getMonth() + 1).toString().padStart(2, '0');
    const random = Math.random().toString(36).substring(2, 6).toUpperCase();
    return `${prefix}-${year}${month}-${random}`;
  }

  // ========== Tipos de Inspección ==========
  async findTiposInspeccion(ctx: ServiceContext): Promise<TipoInspeccion[]> {
    return this.tipoInspeccionRepository.find({
      where: { tenantId: ctx.tenantId, activo: true } as FindOptionsWhere<TipoInspeccion>,
      order: { nombre: 'ASC' },
    });
  }

  // ========== Inspecciones ==========
  async findInspecciones(
    ctx: ServiceContext,
    filters: InspeccionFilters = {},
    page: number = 1,
    limit: number = 20
  ): Promise<PaginatedResult<Inspeccion>> {
    const skip = (page - 1) * limit;

    const queryBuilder = this.inspeccionRepository
      .createQueryBuilder('inspeccion')
      .leftJoinAndSelect('inspeccion.tipoInspeccion', 'tipoInspeccion')
      .leftJoinAndSelect('inspeccion.fraccionamiento', 'fraccionamiento')
      .leftJoinAndSelect('inspeccion.inspector', 'inspector')
      .where('inspeccion.tenant_id = :tenantId', { tenantId: ctx.tenantId });

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

    if (filters.tipoInspeccionId) {
      queryBuilder.andWhere('inspeccion.tipo_inspeccion_id = :tipoInspeccionId', {
        tipoInspeccionId: filters.tipoInspeccionId,
      });
    }

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

    if (filters.inspectorId) {
      queryBuilder.andWhere('inspeccion.inspector_id = :inspectorId', {
        inspectorId: filters.inspectorId,
      });
    }

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

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

    queryBuilder
      .orderBy('inspeccion.fecha_inicio', 'DESC')
      .skip(skip)
      .take(limit);

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

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

  async findInspeccionById(ctx: ServiceContext, id: string): Promise<Inspeccion | null> {
    return this.inspeccionRepository.findOne({
      where: { id, tenantId: ctx.tenantId } as FindOptionsWhere<Inspeccion>,
      relations: ['tipoInspeccion', 'fraccionamiento', 'inspector'],
    });
  }

  async findInspeccionWithDetails(ctx: ServiceContext, id: string): Promise<Inspeccion | null> {
    return this.inspeccionRepository.findOne({
      where: { id, tenantId: ctx.tenantId } as FindOptionsWhere<Inspeccion>,
      relations: [
        'tipoInspeccion',
        'fraccionamiento',
        'inspector',
        'evaluaciones',
        'evaluaciones.checklistItem',
        'hallazgos',
        'hallazgos.responsableCorreccion',
      ],
    });
  }

  async createInspeccion(ctx: ServiceContext, dto: CreateInspeccionDto): Promise<Inspeccion> {
    const inspeccion = this.inspeccionRepository.create({
      tenantId: ctx.tenantId,
      tipoInspeccionId: dto.tipoInspeccionId,
      fraccionamientoId: dto.fraccionamientoId,
      inspectorId: dto.inspectorId,
      programaId: dto.programaId,
      fechaInicio: new Date(),
      estado: 'borrador',
    });

    return this.inspeccionRepository.save(inspeccion);
  }

  async updateInspeccionEstado(ctx: ServiceContext, id: string, estado: string): Promise<Inspeccion | null> {
    const inspeccion = await this.findInspeccionById(ctx, id);
    if (!inspeccion) return null;

    inspeccion.estado = estado;
    if (estado === 'completado' || estado === 'finalizado') {
      inspeccion.fechaFin = new Date();
    }

    return this.inspeccionRepository.save(inspeccion);
  }

  async updateInspeccionObservaciones(ctx: ServiceContext, id: string, observaciones: string): Promise<Inspeccion | null> {
    const inspeccion = await this.findInspeccionById(ctx, id);
    if (!inspeccion) return null;

    inspeccion.observacionesGenerales = observaciones;
    return this.inspeccionRepository.save(inspeccion);
  }

  // ========== Hallazgos ==========
  async findHallazgos(
    ctx: ServiceContext,
    filters: HallazgoFilters = {},
    page: number = 1,
    limit: number = 20
  ): Promise<PaginatedResult<Hallazgo>> {
    const skip = (page - 1) * limit;

    const queryBuilder = this.hallazgoRepository
      .createQueryBuilder('hallazgo')
      .leftJoinAndSelect('hallazgo.inspeccion', 'inspeccion')
      .leftJoinAndSelect('inspeccion.fraccionamiento', 'fraccionamiento')
      .leftJoinAndSelect('hallazgo.responsableCorreccion', 'responsable')
      .where('hallazgo.tenant_id = :tenantId', { tenantId: ctx.tenantId });

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

    if (filters.gravedad) {
      queryBuilder.andWhere('hallazgo.gravedad = :gravedad', { gravedad: filters.gravedad });
    }

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

    if (filters.responsableId) {
      queryBuilder.andWhere('hallazgo.responsable_correccion_id = :responsableId', {
        responsableId: filters.responsableId,
      });
    }

    if (filters.vencidos) {
      queryBuilder.andWhere('hallazgo.fecha_limite < :today', { today: new Date() });
      queryBuilder.andWhere('hallazgo.estado NOT IN (:...cerrados)', { cerrados: ['cerrado'] });
    }

    queryBuilder
      .orderBy('hallazgo.fecha_limite', 'ASC')
      .skip(skip)
      .take(limit);

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

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

  async createHallazgo(ctx: ServiceContext, inspeccionId: string, dto: CreateHallazgoDto): Promise<Hallazgo> {
    const inspeccion = await this.findInspeccionById(ctx, inspeccionId);
    if (!inspeccion) throw new Error('Inspección no encontrada');

    const hallazgo = this.hallazgoRepository.create({
      tenantId: ctx.tenantId,
      inspeccionId,
      folio: this.generateFolio('HAL'),
      evaluacionId: dto.evaluacionId,
      gravedad: dto.gravedad,
      tipo: dto.tipo,
      descripcion: dto.descripcion,
      ubicacionDescripcion: dto.ubicacionDescripcion,
      responsableCorreccionId: dto.responsableCorreccionId,
      fechaLimite: dto.fechaLimite,
      estado: 'abierto',
    });

    return this.hallazgoRepository.save(hallazgo);
  }

  async registrarCorreccion(
    ctx: ServiceContext,
    hallazgoId: string,
    descripcionCorreccion: string
  ): Promise<Hallazgo | null> {
    const hallazgo = await this.hallazgoRepository.findOne({
      where: { id: hallazgoId, tenantId: ctx.tenantId } as FindOptionsWhere<Hallazgo>,
    });
    if (!hallazgo) return null;

    hallazgo.estado = 'verificando';
    hallazgo.descripcionCorreccion = descripcionCorreccion;
    hallazgo.fechaCorreccion = new Date();

    return this.hallazgoRepository.save(hallazgo);
  }

  async verificarHallazgo(
    ctx: ServiceContext,
    hallazgoId: string,
    verificadorId: string,
    aprobado: boolean
  ): Promise<Hallazgo | null> {
    const hallazgo = await this.hallazgoRepository.findOne({
      where: { id: hallazgoId, tenantId: ctx.tenantId } as FindOptionsWhere<Hallazgo>,
    });
    if (!hallazgo) return null;

    hallazgo.verificadorId = verificadorId;
    hallazgo.fechaVerificacion = new Date();
    hallazgo.estado = aprobado ? 'cerrado' : 'reabierto';

    return this.hallazgoRepository.save(hallazgo);
  }

  // ========== Estadísticas ==========
  async getStats(ctx: ServiceContext, fraccionamientoId?: string): Promise<InspeccionStats> {
    const inspQuery = this.inspeccionRepository.createQueryBuilder('i')
      .where('i.tenant_id = :tenantId', { tenantId: ctx.tenantId });
    if (fraccionamientoId) {
      inspQuery.andWhere('i.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId });
    }
    const totalInspecciones = await inspQuery.getCount();

    const hallazgosAbiertos = await this.hallazgoRepository
      .createQueryBuilder('h')
      .where('h.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('h.estado NOT IN (:...cerrados)', { cerrados: ['cerrado'] })
      .getCount();

    const hallazgosVencidos = await this.hallazgoRepository
      .createQueryBuilder('h')
      .where('h.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('h.fecha_limite < :today', { today: new Date() })
      .andWhere('h.estado NOT IN (:...cerrados)', { cerrados: ['cerrado'] })
      .getCount();

    return {
      totalInspecciones,
      hallazgosAbiertos,
      hallazgosVencidos,
    };
  }
}
