/**
 * PuestoService - Servicio para catálogo de puestos
 *
 * Gestión de puestos de trabajo con CRUD básico.
 *
 * @module HR
 */

import { DataSource, Repository, FindOptionsWhere } from 'typeorm';
import { Puesto } from '../entities/puesto.entity';

interface ServiceContext {
  tenantId: string;
  userId?: string;
}

interface PaginatedResult<T> {
  data: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}

export interface CreatePuestoDto {
  codigo: string;
  nombre: string;
  descripcion?: string;
  nivelRiesgo?: string;
  requiereCapacitacionEspecial?: boolean;
}

export interface UpdatePuestoDto {
  nombre?: string;
  descripcion?: string;
  nivelRiesgo?: string;
  requiereCapacitacionEspecial?: boolean;
  activo?: boolean;
}

export interface PuestoFilters {
  activo?: boolean;
  nivelRiesgo?: string;
  search?: string;
}

export class PuestoService {
  private repository: Repository<Puesto>;

  constructor(dataSource: DataSource) {
    this.repository = dataSource.getRepository(Puesto);
  }

  async findAll(
    ctx: ServiceContext,
    filters: PuestoFilters = {},
    page: number = 1,
    limit: number = 20
  ): Promise<PaginatedResult<Puesto>> {
    const skip = (page - 1) * limit;

    const queryBuilder = this.repository
      .createQueryBuilder('puesto')
      .where('puesto.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (filters.activo !== undefined) {
      queryBuilder.andWhere('puesto.activo = :activo', { activo: filters.activo });
    }

    if (filters.nivelRiesgo) {
      queryBuilder.andWhere('puesto.nivel_riesgo = :nivelRiesgo', { nivelRiesgo: filters.nivelRiesgo });
    }

    if (filters.search) {
      queryBuilder.andWhere(
        '(puesto.codigo ILIKE :search OR puesto.nombre ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

    queryBuilder
      .orderBy('puesto.nombre', 'ASC')
      .skip(skip)
      .take(limit);

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

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

  async findById(ctx: ServiceContext, id: string): Promise<Puesto | null> {
    return this.repository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<Puesto>,
      relations: ['empleados'],
    });
  }

  async findByCodigo(ctx: ServiceContext, codigo: string): Promise<Puesto | null> {
    return this.repository.findOne({
      where: {
        codigo,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<Puesto>,
    });
  }

  async create(ctx: ServiceContext, dto: CreatePuestoDto): Promise<Puesto> {
    const existing = await this.findByCodigo(ctx, dto.codigo);
    if (existing) {
      throw new Error(`Puesto with codigo ${dto.codigo} already exists`);
    }

    const puesto = this.repository.create({
      tenantId: ctx.tenantId,
      codigo: dto.codigo,
      nombre: dto.nombre,
      descripcion: dto.descripcion,
      nivelRiesgo: dto.nivelRiesgo,
      requiereCapacitacionEspecial: dto.requiereCapacitacionEspecial || false,
      activo: true,
    });

    return this.repository.save(puesto);
  }

  async update(ctx: ServiceContext, id: string, dto: UpdatePuestoDto): Promise<Puesto | null> {
    const existing = await this.findById(ctx, id);
    if (!existing) {
      return null;
    }

    const updated = this.repository.merge(existing, dto);
    return this.repository.save(updated);
  }

  async toggleActive(ctx: ServiceContext, id: string): Promise<Puesto | null> {
    const existing = await this.findById(ctx, id);
    if (!existing) {
      return null;
    }

    existing.activo = !existing.activo;
    return this.repository.save(existing);
  }

  async getActiveCount(ctx: ServiceContext): Promise<number> {
    return this.repository.count({
      where: {
        tenantId: ctx.tenantId,
        activo: true,
      } as FindOptionsWhere<Puesto>,
    });
  }
}
