"use strict";
/**
 * IncidenteService - Servicio para gestión de incidentes HSE
 *
 * Gestión de incidentes de seguridad con workflow y relaciones.
 * Workflow: abierto -> en_investigacion -> cerrado
 *
 * @module HSE
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.IncidenteService = void 0;
class IncidenteService {
    incidenteRepository;
    involucradoRepository;
    accionRepository;
    constructor(incidenteRepository, involucradoRepository, accionRepository) {
        this.incidenteRepository = incidenteRepository;
        this.involucradoRepository = involucradoRepository;
        this.accionRepository = accionRepository;
    }
    generateFolio() {
        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 `INC-${year}${month}-${random}`;
    }
    async findWithFilters(ctx, filters = {}, page = 1, limit = 20) {
        const skip = (page - 1) * limit;
        const queryBuilder = this.incidenteRepository
            .createQueryBuilder('incidente')
            .leftJoinAndSelect('incidente.fraccionamiento', 'fraccionamiento')
            .leftJoinAndSelect('incidente.createdBy', 'createdBy')
            .where('incidente.tenant_id = :tenantId', { tenantId: ctx.tenantId });
        if (filters.fraccionamientoId) {
            queryBuilder.andWhere('incidente.fraccionamiento_id = :fraccionamientoId', {
                fraccionamientoId: filters.fraccionamientoId,
            });
        }
        if (filters.tipo) {
            queryBuilder.andWhere('incidente.tipo = :tipo', { tipo: filters.tipo });
        }
        if (filters.gravedad) {
            queryBuilder.andWhere('incidente.gravedad = :gravedad', { gravedad: filters.gravedad });
        }
        if (filters.estado) {
            queryBuilder.andWhere('incidente.estado = :estado', { estado: filters.estado });
        }
        if (filters.dateFrom) {
            queryBuilder.andWhere('incidente.fecha_hora >= :dateFrom', { dateFrom: filters.dateFrom });
        }
        if (filters.dateTo) {
            queryBuilder.andWhere('incidente.fecha_hora <= :dateTo', { dateTo: filters.dateTo });
        }
        queryBuilder
            .orderBy('incidente.fecha_hora', 'DESC')
            .skip(skip)
            .take(limit);
        const [data, total] = await queryBuilder.getManyAndCount();
        return {
            data,
            meta: {
                total,
                page,
                limit,
                totalPages: Math.ceil(total / limit),
            },
        };
    }
    async findById(ctx, id) {
        return this.incidenteRepository.findOne({
            where: {
                id,
                tenantId: ctx.tenantId,
            },
        });
    }
    async findWithDetails(ctx, id) {
        return this.incidenteRepository.findOne({
            where: {
                id,
                tenantId: ctx.tenantId,
            },
            relations: [
                'fraccionamiento',
                'createdBy',
                'involucrados',
                'involucrados.employee',
                'acciones',
                'acciones.responsable',
            ],
        });
    }
    async create(ctx, dto) {
        const incidente = this.incidenteRepository.create({
            tenantId: ctx.tenantId,
            createdById: ctx.userId,
            folio: this.generateFolio(),
            fechaHora: dto.fechaHora,
            fraccionamientoId: dto.fraccionamientoId,
            ubicacionDescripcion: dto.ubicacionDescripcion,
            tipo: dto.tipo,
            gravedad: dto.gravedad,
            descripcion: dto.descripcion,
            causaInmediata: dto.causaInmediata,
            causaBasica: dto.causaBasica,
            estado: 'abierto',
        });
        return this.incidenteRepository.save(incidente);
    }
    async update(ctx, id, dto) {
        const existing = await this.findById(ctx, id);
        if (!existing) {
            return null;
        }
        if (existing.estado === 'cerrado') {
            throw new Error('Cannot update a closed incident');
        }
        const updated = this.incidenteRepository.merge(existing, dto);
        return this.incidenteRepository.save(updated);
    }
    async startInvestigation(ctx, id) {
        const existing = await this.findById(ctx, id);
        if (!existing) {
            return null;
        }
        if (existing.estado !== 'abierto') {
            throw new Error('Can only start investigation on open incidents');
        }
        existing.estado = 'en_investigacion';
        return this.incidenteRepository.save(existing);
    }
    async closeIncident(ctx, id) {
        const existing = await this.findWithDetails(ctx, id);
        if (!existing) {
            return null;
        }
        if (existing.estado === 'cerrado') {
            throw new Error('Incident is already closed');
        }
        // Check if all actions are completed or verified
        const pendingActions = existing.acciones?.filter((a) => a.estado !== 'completada' && a.estado !== 'verificada');
        if (pendingActions && pendingActions.length > 0) {
            throw new Error('Cannot close incident with pending actions');
        }
        existing.estado = 'cerrado';
        return this.incidenteRepository.save(existing);
    }
    async addInvolucrado(ctx, incidenteId, dto) {
        const incidente = await this.findById(ctx, incidenteId);
        if (!incidente) {
            throw new Error('Incidente not found');
        }
        const involucrado = this.involucradoRepository.create({
            incidenteId,
            employeeId: dto.employeeId,
            rol: dto.rol,
            descripcionLesion: dto.descripcionLesion,
            parteCuerpo: dto.parteCuerpo,
            diasIncapacidad: dto.diasIncapacidad || 0,
        });
        return this.involucradoRepository.save(involucrado);
    }
    async removeInvolucrado(ctx, incidenteId, involucradoId) {
        const incidente = await this.findById(ctx, incidenteId);
        if (!incidente) {
            return false;
        }
        const result = await this.involucradoRepository.delete({
            id: involucradoId,
            incidenteId,
        });
        return (result.affected ?? 0) > 0;
    }
    async addAccion(ctx, incidenteId, dto) {
        const incidente = await this.findById(ctx, incidenteId);
        if (!incidente) {
            throw new Error('Incidente not found');
        }
        if (incidente.estado === 'cerrado') {
            throw new Error('Cannot add actions to a closed incident');
        }
        const accion = this.accionRepository.create({
            incidenteId,
            descripcion: dto.descripcion,
            tipo: dto.tipo,
            responsableId: dto.responsableId,
            fechaCompromiso: dto.fechaCompromiso,
            estado: 'pendiente',
        });
        return this.accionRepository.save(accion);
    }
    async updateAccion(ctx, incidenteId, accionId, dto) {
        const incidente = await this.findById(ctx, incidenteId);
        if (!incidente) {
            return null;
        }
        const accion = await this.accionRepository.findOne({
            where: { id: accionId, incidenteId },
        });
        if (!accion) {
            return null;
        }
        // If completing the action, set the close date
        if (dto.estado === 'completada' && accion.estado !== 'completada') {
            dto.fechaCompromiso = dto.fechaCompromiso; // keep existing
            accion.fechaCierre = new Date();
        }
        const updated = this.accionRepository.merge(accion, dto);
        return this.accionRepository.save(updated);
    }
    async getStats(ctx, fraccionamientoId) {
        const queryBuilder = this.incidenteRepository
            .createQueryBuilder('incidente')
            .where('incidente.tenant_id = :tenantId', { tenantId: ctx.tenantId });
        if (fraccionamientoId) {
            queryBuilder.andWhere('incidente.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId });
        }
        const total = await queryBuilder.getCount();
        const porTipo = await this.incidenteRepository
            .createQueryBuilder('incidente')
            .select('incidente.tipo', 'tipo')
            .addSelect('COUNT(*)', 'count')
            .where('incidente.tenant_id = :tenantId', { tenantId: ctx.tenantId })
            .groupBy('incidente.tipo')
            .getRawMany();
        const porGravedad = await this.incidenteRepository
            .createQueryBuilder('incidente')
            .select('incidente.gravedad', 'gravedad')
            .addSelect('COUNT(*)', 'count')
            .where('incidente.tenant_id = :tenantId', { tenantId: ctx.tenantId })
            .groupBy('incidente.gravedad')
            .getRawMany();
        const porEstado = await this.incidenteRepository
            .createQueryBuilder('incidente')
            .select('incidente.estado', 'estado')
            .addSelect('COUNT(*)', 'count')
            .where('incidente.tenant_id = :tenantId', { tenantId: ctx.tenantId })
            .groupBy('incidente.estado')
            .getRawMany();
        // Calculate days since last accident
        const lastAccident = await this.incidenteRepository.findOne({
            where: {
                tenantId: ctx.tenantId,
                tipo: 'accidente',
            },
            order: { fechaHora: 'DESC' },
        });
        let diasSinAccidente = 0;
        if (lastAccident) {
            const diff = Date.now() - new Date(lastAccident.fechaHora).getTime();
            diasSinAccidente = Math.floor(diff / (1000 * 60 * 60 * 24));
        }
        return {
            total,
            porTipo: porTipo.map((p) => ({ tipo: p.tipo, count: parseInt(p.count, 10) })),
            porGravedad: porGravedad.map((p) => ({ gravedad: p.gravedad, count: parseInt(p.count, 10) })),
            porEstado: porEstado.map((p) => ({ estado: p.estado, count: parseInt(p.count, 10) })),
            diasSinAccidente,
        };
    }
}
exports.IncidenteService = IncidenteService;
//# sourceMappingURL=incidente.service.js.map