/**
 * Puestos (Positions) Service
 * @module HR
 */

import { Repository, FindOptionsWhere, ILike } from 'typeorm';
import { Puesto } from '../entities/puesto.entity';
import { CreatePuestoDto, UpdatePuestoDto } from '../dto';

export interface PuestoSearchParams {
  tenantId: string;
  search?: string;
  activo?: boolean;
  nivelRiesgo?: string;
  requiereCapacitacionEspecial?: boolean;
  limit?: number;
  offset?: number;
}

export class PuestosService {
  constructor(private readonly puestoRepository: Repository<Puesto>) {}

  /**
   * Find all puestos with filters
   */
  async findAll(params: PuestoSearchParams): Promise<{ data: Puesto[]; total: number }> {
    const {
      tenantId,
      search,
      activo,
      nivelRiesgo,
      requiereCapacitacionEspecial,
      limit = 50,
      offset = 0,
    } = params;

    const where: FindOptionsWhere<Puesto>[] = [];
    const baseWhere: FindOptionsWhere<Puesto> = { tenantId };

    if (activo !== undefined) {
      baseWhere.activo = activo;
    }

    if (nivelRiesgo) {
      baseWhere.nivelRiesgo = nivelRiesgo;
    }

    if (requiereCapacitacionEspecial !== undefined) {
      baseWhere.requiereCapacitacionEspecial = requiereCapacitacionEspecial;
    }

    if (search) {
      where.push(
        { ...baseWhere, codigo: ILike(`%${search}%`) },
        { ...baseWhere, nombre: ILike(`%${search}%`) }
      );
    } else {
      where.push(baseWhere);
    }

    const [data, total] = await this.puestoRepository.findAndCount({
      where,
      take: limit,
      skip: offset,
      order: { nombre: 'ASC' },
    });

    return { data, total };
  }

  /**
   * Find one puesto by ID
   */
  async findOne(id: string, tenantId: string): Promise<Puesto | null> {
    return this.puestoRepository.findOne({
      where: { id, tenantId },
      relations: ['empleados'],
    });
  }

  /**
   * Find puesto by code
   */
  async findByCode(codigo: string, tenantId: string): Promise<Puesto | null> {
    return this.puestoRepository.findOne({
      where: { codigo, tenantId },
    });
  }

  /**
   * Create new puesto
   */
  async create(tenantId: string, dto: CreatePuestoDto): Promise<Puesto> {
    // Check for existing code
    const existingCode = await this.findByCode(dto.codigo, tenantId);
    if (existingCode) {
      throw new Error('Ya existe un puesto con este codigo');
    }

    const puesto = this.puestoRepository.create({
      ...dto,
      tenantId,
    });

    return this.puestoRepository.save(puesto);
  }

  /**
   * Update puesto
   */
  async update(id: string, tenantId: string, dto: UpdatePuestoDto): Promise<Puesto | null> {
    const puesto = await this.findOne(id, tenantId);
    if (!puesto) return null;

    // If changing code, check for duplicates
    if (dto.codigo && dto.codigo !== puesto.codigo) {
      const existing = await this.findByCode(dto.codigo, tenantId);
      if (existing) {
        throw new Error('Ya existe un puesto con este codigo');
      }
    }

    Object.assign(puesto, dto);
    return this.puestoRepository.save(puesto);
  }

  /**
   * Delete puesto
   */
  async delete(id: string, tenantId: string): Promise<boolean> {
    const puesto = await this.findOne(id, tenantId);
    if (!puesto) return false;

    // Check if has employees assigned
    if (puesto.empleados && puesto.empleados.length > 0) {
      throw new Error('No se puede eliminar un puesto con empleados asignados');
    }

    const result = await this.puestoRepository.delete(id);
    return (result.affected ?? 0) > 0;
  }

  /**
   * Get active puestos
   */
  async getActive(tenantId: string): Promise<Puesto[]> {
    return this.puestoRepository.find({
      where: { tenantId, activo: true },
      order: { nombre: 'ASC' },
    });
  }

  /**
   * Get puestos by risk level
   */
  async getByRiskLevel(tenantId: string, nivelRiesgo: string): Promise<Puesto[]> {
    return this.puestoRepository.find({
      where: { tenantId, nivelRiesgo, activo: true },
      order: { nombre: 'ASC' },
    });
  }

  /**
   * Get puestos requiring special training
   */
  async getRequiringSpecialTraining(tenantId: string): Promise<Puesto[]> {
    return this.puestoRepository.find({
      where: { tenantId, requiereCapacitacionEspecial: true, activo: true },
      order: { nombre: 'ASC' },
    });
  }

  /**
   * Activate puesto
   */
  async activate(id: string, tenantId: string): Promise<Puesto | null> {
    const puesto = await this.findOne(id, tenantId);
    if (!puesto) return null;

    puesto.activo = true;
    return this.puestoRepository.save(puesto);
  }

  /**
   * Deactivate puesto
   */
  async deactivate(id: string, tenantId: string): Promise<Puesto | null> {
    const puesto = await this.findOne(id, tenantId);
    if (!puesto) return null;

    puesto.activo = false;
    return this.puestoRepository.save(puesto);
  }

  /**
   * Get employee count by puesto
   */
  async getEmployeeCount(tenantId: string): Promise<Array<{ puestoId: string; nombre: string; count: number }>> {
    const result = await this.puestoRepository
      .createQueryBuilder('puesto')
      .leftJoin('puesto.empleados', 'empleado', 'empleado.estado = :estado', { estado: 'activo' })
      .select('puesto.id', 'puestoId')
      .addSelect('puesto.nombre', 'nombre')
      .addSelect('COUNT(empleado.id)', 'count')
      .where('puesto.tenant_id = :tenantId', { tenantId })
      .andWhere('puesto.activo = true')
      .groupBy('puesto.id')
      .addGroupBy('puesto.nombre')
      .getRawMany();

    return result.map(row => ({
      puestoId: row.puestoId,
      nombre: row.nombre,
      count: parseInt(row.count, 10),
    }));
  }
}
