"use strict";
/**
 * EmployeeService - Servicio para gestión de empleados
 *
 * CRUD de empleados con gestión de estado y asignaciones a obras.
 *
 * @module HR
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.EmployeeService = void 0;
class EmployeeService {
    employeeRepository;
    asignacionRepository;
    constructor(employeeRepository, asignacionRepository) {
        this.employeeRepository = employeeRepository;
        this.asignacionRepository = asignacionRepository;
    }
    async findWithFilters(ctx, filters = {}, page = 1, limit = 20) {
        const skip = (page - 1) * limit;
        const queryBuilder = this.employeeRepository
            .createQueryBuilder('employee')
            .leftJoinAndSelect('employee.puesto', 'puesto')
            .where('employee.tenant_id = :tenantId', { tenantId: ctx.tenantId });
        if (filters.estado) {
            queryBuilder.andWhere('employee.estado = :estado', { estado: filters.estado });
        }
        if (filters.puestoId) {
            queryBuilder.andWhere('employee.puesto_id = :puestoId', { puestoId: filters.puestoId });
        }
        if (filters.departamento) {
            queryBuilder.andWhere('employee.departamento = :departamento', { departamento: filters.departamento });
        }
        if (filters.fraccionamientoId) {
            queryBuilder
                .innerJoin('employee.asignaciones', 'asig')
                .andWhere('asig.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId: filters.fraccionamientoId })
                .andWhere('asig.activo = true');
        }
        if (filters.search) {
            queryBuilder.andWhere('(employee.codigo ILIKE :search OR employee.nombre ILIKE :search OR employee.apellido_paterno ILIKE :search OR employee.curp ILIKE :search)', { search: `%${filters.search}%` });
        }
        queryBuilder
            .orderBy('employee.apellido_paterno', 'ASC')
            .addOrderBy('employee.nombre', 'ASC')
            .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.employeeRepository.findOne({
            where: {
                id,
                tenantId: ctx.tenantId,
            },
            relations: ['puesto', 'asignaciones', 'asignaciones.fraccionamiento'],
        });
    }
    async findByCodigo(ctx, codigo) {
        return this.employeeRepository.findOne({
            where: {
                codigo,
                tenantId: ctx.tenantId,
            },
        });
    }
    async findByCurp(ctx, curp) {
        return this.employeeRepository.findOne({
            where: {
                curp,
                tenantId: ctx.tenantId,
            },
        });
    }
    async create(ctx, dto) {
        // Validate unique codigo
        const existingCodigo = await this.findByCodigo(ctx, dto.codigo);
        if (existingCodigo) {
            throw new Error(`Employee with codigo ${dto.codigo} already exists`);
        }
        // Validate unique CURP if provided
        if (dto.curp) {
            const existingCurp = await this.findByCurp(ctx, dto.curp);
            if (existingCurp) {
                throw new Error(`Employee with CURP ${dto.curp} already exists`);
            }
        }
        const employee = this.employeeRepository.create({
            tenantId: ctx.tenantId,
            createdById: ctx.userId,
            estado: 'activo',
            ...dto,
        });
        return this.employeeRepository.save(employee);
    }
    async update(ctx, id, dto) {
        const existing = await this.findById(ctx, id);
        if (!existing) {
            return null;
        }
        // Validate unique CURP if changed
        if (dto.curp && dto.curp !== existing.curp) {
            const existingCurp = await this.findByCurp(ctx, dto.curp);
            if (existingCurp) {
                throw new Error(`Employee with CURP ${dto.curp} already exists`);
            }
        }
        const updated = this.employeeRepository.merge(existing, dto);
        return this.employeeRepository.save(updated);
    }
    async changeStatus(ctx, id, estado, fechaBaja) {
        const existing = await this.findById(ctx, id);
        if (!existing) {
            return null;
        }
        existing.estado = estado;
        if (estado === 'baja' && fechaBaja) {
            existing.fechaBaja = fechaBaja;
        }
        return this.employeeRepository.save(existing);
    }
    async assignToFraccionamiento(ctx, employeeId, dto) {
        const employee = await this.findById(ctx, employeeId);
        if (!employee) {
            throw new Error('Employee not found');
        }
        // Deactivate previous active assignment to same fraccionamiento
        await this.asignacionRepository.update({
            tenantId: ctx.tenantId,
            employeeId,
            fraccionamientoId: dto.fraccionamientoId,
            activo: true,
        }, { activo: false, fechaFin: new Date() });
        const asignacion = this.asignacionRepository.create({
            tenantId: ctx.tenantId,
            employeeId,
            fraccionamientoId: dto.fraccionamientoId,
            fechaInicio: dto.fechaInicio,
            fechaFin: dto.fechaFin,
            rol: dto.rol,
            activo: true,
        });
        return this.asignacionRepository.save(asignacion);
    }
    async removeFromFraccionamiento(ctx, employeeId, fraccionamientoId) {
        const result = await this.asignacionRepository.update({
            tenantId: ctx.tenantId,
            employeeId,
            fraccionamientoId,
            activo: true,
        }, { activo: false, fechaFin: new Date() });
        return (result.affected ?? 0) > 0;
    }
    async getEmployeesByFraccionamiento(ctx, fraccionamientoId) {
        const asignaciones = await this.asignacionRepository.find({
            where: {
                tenantId: ctx.tenantId,
                fraccionamientoId,
                activo: true,
            },
            relations: ['employee', 'employee.puesto'],
        });
        return asignaciones.map(a => a.employee);
    }
    async getStats(ctx) {
        const [total, activos, inactivos, bajas] = await Promise.all([
            this.employeeRepository.count({
                where: { tenantId: ctx.tenantId },
            }),
            this.employeeRepository.count({
                where: { tenantId: ctx.tenantId, estado: 'activo' },
            }),
            this.employeeRepository.count({
                where: { tenantId: ctx.tenantId, estado: 'inactivo' },
            }),
            this.employeeRepository.count({
                where: { tenantId: ctx.tenantId, estado: 'baja' },
            }),
        ]);
        const porDepartamento = await this.employeeRepository
            .createQueryBuilder('employee')
            .select('employee.departamento', 'departamento')
            .addSelect('COUNT(*)', 'count')
            .where('employee.tenant_id = :tenantId', { tenantId: ctx.tenantId })
            .andWhere('employee.estado = :estado', { estado: 'activo' })
            .groupBy('employee.departamento')
            .getRawMany();
        return {
            total,
            activos,
            inactivos,
            bajas,
            porDepartamento: porDepartamento.map(p => ({
                departamento: p.departamento || 'Sin departamento',
                count: parseInt(p.count, 10),
            })),
        };
    }
}
exports.EmployeeService = EmployeeService;
//# sourceMappingURL=employee.service.js.map