/**
 * PermisoTrabajoService - Servicio para permisos de trabajo
 *
 * RF-MAA017-007: Gestión de permisos de trabajo de alto riesgo
 *
 * @module HSE
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { TipoPermisoTrabajo } from '../entities/tipo-permiso-trabajo.entity';
import { PermisoTrabajo, EstadoPermiso } from '../entities/permiso-trabajo.entity';
import { PermisoPersonal } from '../entities/permiso-personal.entity';
import { PermisoAutorizacion } from '../entities/permiso-autorizacion.entity';
import { PermisoChecklist } from '../entities/permiso-checklist.entity';
import { PermisoMonitoreo } from '../entities/permiso-monitoreo.entity';
import { PermisoEvento } from '../entities/permiso-evento.entity';
import { PermisoDocumento } from '../entities/permiso-documento.entity';
import { ServiceContext, PaginatedResult } from '../../../shared/services/base.service';

export interface PermisoFilters {
  fraccionamientoId?: string;
  tipoPermisoId?: string;
  estado?: EstadoPermiso;
  solicitanteId?: string;
  vigentes?: boolean;
  dateFrom?: Date;
  dateTo?: Date;
}

export interface CreatePermisoDto {
  fraccionamientoId: string;
  tipoPermisoId: string;
  descripcionTrabajo: string;
  ubicacion: string;
  fechaInicioProgramada: Date;
  fechaFinProgramada: Date;
  observaciones?: string;
}

export interface PermisoStats {
  totalPermisos: number;
  permisosVigentes: number;
  permisosPorEstado: { estado: string; count: number }[];
}

export class PermisoTrabajoService {
  constructor(
    private readonly tipoPermisoRepository: Repository<TipoPermisoTrabajo>,
    private readonly permisoRepository: Repository<PermisoTrabajo>,
    private readonly personalRepository: Repository<PermisoPersonal>,
    private readonly autorizacionRepository: Repository<PermisoAutorizacion>,
    private readonly checklistRepository: Repository<PermisoChecklist>,
    private readonly monitoreoRepository: Repository<PermisoMonitoreo>,
    private readonly eventoRepository: Repository<PermisoEvento>,
    private readonly documentoRepository: Repository<PermisoDocumento>
  ) {}

  private generateFolio(): 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 `PT-${year}${month}-${random}`;
  }

  // ========== Tipos de Permiso ==========
  async findTiposPermiso(ctx: ServiceContext): Promise<TipoPermisoTrabajo[]> {
    return this.tipoPermisoRepository.find({
      where: { tenantId: ctx.tenantId, activo: true } as FindOptionsWhere<TipoPermisoTrabajo>,
      order: { nombre: 'ASC' },
    });
  }

  // ========== Permisos de Trabajo ==========
  async findPermisos(
    ctx: ServiceContext,
    filters: PermisoFilters = {},
    page: number = 1,
    limit: number = 20
  ): Promise<PaginatedResult<PermisoTrabajo>> {
    const skip = (page - 1) * limit;

    const queryBuilder = this.permisoRepository
      .createQueryBuilder('p')
      .leftJoinAndSelect('p.tipoPermiso', 'tipo')
      .leftJoinAndSelect('p.fraccionamiento', 'fraccionamiento')
      .leftJoinAndSelect('p.solicitante', 'solicitante')
      .where('p.tenant_id = :tenantId', { tenantId: ctx.tenantId });

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

    if (filters.tipoPermisoId) {
      queryBuilder.andWhere('p.tipo_permiso_id = :tipoPermisoId', {
        tipoPermisoId: filters.tipoPermisoId,
      });
    }

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

    if (filters.solicitanteId) {
      queryBuilder.andWhere('p.solicitante_id = :solicitanteId', {
        solicitanteId: filters.solicitanteId,
      });
    }

    if (filters.vigentes) {
      const now = new Date();
      queryBuilder.andWhere('p.fecha_inicio_programada <= :now', { now });
      queryBuilder.andWhere('p.fecha_fin_programada >= :now', { now });
      queryBuilder.andWhere('p.estado IN (:...activos)', { activos: ['autorizado', 'en_ejecucion'] });
    }

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

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

    queryBuilder
      .orderBy('p.fecha_inicio_programada', 'DESC')
      .skip(skip)
      .take(limit);

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

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

  async findPermisoById(ctx: ServiceContext, id: string): Promise<PermisoTrabajo | null> {
    return this.permisoRepository.findOne({
      where: { id, tenantId: ctx.tenantId } as FindOptionsWhere<PermisoTrabajo>,
      relations: ['tipoPermiso', 'fraccionamiento', 'solicitante'],
    });
  }

  async findPermisoWithDetails(ctx: ServiceContext, id: string): Promise<PermisoTrabajo | null> {
    return this.permisoRepository.findOne({
      where: { id, tenantId: ctx.tenantId } as FindOptionsWhere<PermisoTrabajo>,
      relations: [
        'tipoPermiso',
        'fraccionamiento',
        'solicitante',
        'personal',
        'personal.employee',
        'autorizaciones',
        'autorizaciones.autorizador',
        'checklist',
        'monitoreos',
        'eventos',
        'documentos',
      ],
    });
  }

  async createPermiso(ctx: ServiceContext, dto: CreatePermisoDto): Promise<PermisoTrabajo> {
    const permiso = this.permisoRepository.create({
      tenantId: ctx.tenantId,
      folio: this.generateFolio(),
      tipoPermisoId: dto.tipoPermisoId,
      fraccionamientoId: dto.fraccionamientoId,
      solicitanteId: ctx.userId,
      descripcionTrabajo: dto.descripcionTrabajo,
      ubicacion: dto.ubicacion,
      fechaInicioProgramada: dto.fechaInicioProgramada,
      fechaFinProgramada: dto.fechaFinProgramada,
      observaciones: dto.observaciones,
      estado: 'borrador',
    });

    return this.permisoRepository.save(permiso);
  }

  async updatePermisoEstado(ctx: ServiceContext, id: string, estado: EstadoPermiso, motivo?: string): Promise<PermisoTrabajo | null> {
    const permiso = await this.findPermisoById(ctx, id);
    if (!permiso) return null;

    permiso.estado = estado;

    if (estado === 'rechazado' && motivo) {
      permiso.motivoRechazo = motivo;
    }
    if (estado === 'suspendido' && motivo) {
      permiso.motivoSuspension = motivo;
    }
    if (estado === 'en_ejecucion') {
      permiso.fechaInicioReal = new Date();
    }
    if (estado === 'cerrado') {
      permiso.fechaFinReal = new Date();
    }

    return this.permisoRepository.save(permiso);
  }

  // ========== Personal Autorizado ==========
  async getPersonalByPermiso(ctx: ServiceContext, permisoId: string): Promise<PermisoPersonal[]> {
    const permiso = await this.findPermisoById(ctx, permisoId);
    if (!permiso) return [];

    return this.personalRepository.find({
      where: { permisoId } as FindOptionsWhere<PermisoPersonal>,
      relations: ['employee'],
    });
  }

  // ========== Autorizaciones ==========
  async getAutorizacionesByPermiso(ctx: ServiceContext, permisoId: string): Promise<PermisoAutorizacion[]> {
    const permiso = await this.findPermisoById(ctx, permisoId);
    if (!permiso) return [];

    return this.autorizacionRepository.find({
      where: { permisoId } as FindOptionsWhere<PermisoAutorizacion>,
      relations: ['autorizador'],
      order: { createdAt: 'ASC' },
    });
  }

  // ========== Checklist ==========
  async getChecklistByPermiso(_ctx: ServiceContext, permisoId: string): Promise<PermisoChecklist[]> {
    return this.checklistRepository.find({
      where: { permisoId } as FindOptionsWhere<PermisoChecklist>,
      order: { createdAt: 'ASC' },
    });
  }

  // ========== Monitoreo ==========
  async getMonitoreosByPermiso(_ctx: ServiceContext, permisoId: string): Promise<PermisoMonitoreo[]> {
    return this.monitoreoRepository.find({
      where: { permisoId } as FindOptionsWhere<PermisoMonitoreo>,
      order: { createdAt: 'DESC' },
    });
  }

  // ========== Eventos ==========
  async getEventosByPermiso(_ctx: ServiceContext, permisoId: string): Promise<PermisoEvento[]> {
    return this.eventoRepository.find({
      where: { permisoId } as FindOptionsWhere<PermisoEvento>,
      order: { createdAt: 'DESC' },
    });
  }

  // ========== Documentos ==========
  async getDocumentosByPermiso(_ctx: ServiceContext, permisoId: string): Promise<PermisoDocumento[]> {
    return this.documentoRepository.find({
      where: { permisoId } as FindOptionsWhere<PermisoDocumento>,
      order: { fechaSubida: 'DESC' },
    });
  }

  // ========== Estadísticas ==========
  async getStats(ctx: ServiceContext, fraccionamientoId?: string): Promise<PermisoStats> {
    const baseQuery = this.permisoRepository
      .createQueryBuilder('p')
      .where('p.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (fraccionamientoId) {
      baseQuery.andWhere('p.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId });
    }

    const totalPermisos = await baseQuery.getCount();

    // Permisos vigentes
    const now = new Date();
    const permisosVigentes = await this.permisoRepository
      .createQueryBuilder('p')
      .where('p.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('p.fecha_inicio_programada <= :now', { now })
      .andWhere('p.fecha_fin_programada >= :now', { now })
      .andWhere('p.estado IN (:...activos)', { activos: ['autorizado', 'en_ejecucion'] })
      .getCount();

    // Por estado
    const permisosPorEstado = await this.permisoRepository
      .createQueryBuilder('p')
      .select('p.estado', 'estado')
      .addSelect('COUNT(*)', 'count')
      .where('p.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .groupBy('p.estado')
      .getRawMany();

    return {
      totalPermisos,
      permisosVigentes,
      permisosPorEstado: permisosPorEstado.map((p) => ({ estado: p.estado, count: parseInt(p.count, 10) })),
    };
  }
}
