import { Repository, FindOptionsWhere, ILike, In } from 'typeorm';
import { Operador, TipoLicencia, EstadoOperador } from '../entities';

export interface OperadorSearchParams {
  tenantId: string;
  search?: string;
  estado?: EstadoOperador;
  estados?: EstadoOperador[];
  tipoLicencia?: TipoLicencia;
  sucursalId?: string;
  limit?: number;
  offset?: number;
}

export interface CreateOperadorDto {
  numeroEmpleado: string;
  nombre: string;
  apellidoPaterno: string;
  apellidoMaterno?: string;
  rfc?: string;
  curp?: string;
  nss?: string;
  fechaNacimiento?: Date;
  telefono?: string;
  telefonoEmergencia?: string;
  email?: string;
  direccion?: string;
  ciudad?: string;
  estadoDireccion?: string;
  codigoPostal?: string;
  tipoLicencia: TipoLicencia;
  numeroLicencia: string;
  vigenciaLicencia: Date;
  restriccionesLicencia?: string;
  fechaIngreso?: Date;
  sucursalId?: string;
  salarioBase?: number;
  bonoPorViaje?: number;
  comisionPorcentaje?: number;
  contactoEmergenciaNombre?: string;
  contactoEmergenciaTelefono?: string;
  contactoEmergenciaParentesco?: string;
  tipoSangre?: string;
  alergias?: string;
  condicionesMedicas?: string;
}

export interface UpdateOperadorDto extends Partial<CreateOperadorDto> {
  estado?: EstadoOperador;
}

export class OperadoresService {
  constructor(private readonly operadorRepository: Repository<Operador>) {}

  async findAll(params: OperadorSearchParams): Promise<{ data: Operador[]; total: number }> {
    const {
      tenantId,
      search,
      estado,
      estados,
      tipoLicencia,
      sucursalId,
      limit = 50,
      offset = 0,
    } = params;

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

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

    if (estados && estados.length > 0) {
      baseWhere.estado = In(estados);
    }

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

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

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

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

    return { data, total };
  }

  async findOne(id: string, tenantId: string): Promise<Operador | null> {
    return this.operadorRepository.findOne({
      where: { id, tenantId },
    });
  }

  async findByNumeroEmpleado(numeroEmpleado: string, tenantId: string): Promise<Operador | null> {
    return this.operadorRepository.findOne({
      where: { numeroEmpleado, tenantId },
    });
  }

  async findByLicencia(numeroLicencia: string, tenantId: string): Promise<Operador | null> {
    return this.operadorRepository.findOne({
      where: { numeroLicencia, tenantId },
    });
  }

  async create(tenantId: string, dto: CreateOperadorDto, createdBy: string): Promise<Operador> {
    // Check for existing numero de empleado
    const existingNumero = await this.findByNumeroEmpleado(dto.numeroEmpleado, tenantId);
    if (existingNumero) {
      throw new Error('Ya existe un operador con este número de empleado');
    }

    // Check for existing licencia
    const existingLicencia = await this.findByLicencia(dto.numeroLicencia, tenantId);
    if (existingLicencia) {
      throw new Error('Ya existe un operador con este número de licencia');
    }

    const operador = this.operadorRepository.create({
      ...dto,
      tenantId,
      estado: EstadoOperador.DISPONIBLE,
      createdById: createdBy,
    });

    return this.operadorRepository.save(operador);
  }

  async update(
    id: string,
    tenantId: string,
    dto: UpdateOperadorDto,
    updatedBy: string
  ): Promise<Operador | null> {
    const operador = await this.findOne(id, tenantId);
    if (!operador) return null;

    // If changing numero empleado, check for duplicates
    if (dto.numeroEmpleado && dto.numeroEmpleado !== operador.numeroEmpleado) {
      const existing = await this.findByNumeroEmpleado(dto.numeroEmpleado, tenantId);
      if (existing) {
        throw new Error('Ya existe un operador con este número de empleado');
      }
    }

    // If changing licencia, check for duplicates
    if (dto.numeroLicencia && dto.numeroLicencia !== operador.numeroLicencia) {
      const existing = await this.findByLicencia(dto.numeroLicencia, tenantId);
      if (existing && existing.id !== id) {
        throw new Error('Ya existe un operador con este número de licencia');
      }
    }

    Object.assign(operador, {
      ...dto,
      updatedById: updatedBy,
    });

    return this.operadorRepository.save(operador);
  }

  async cambiarEstado(
    id: string,
    tenantId: string,
    nuevoEstado: EstadoOperador,
    updatedBy: string
  ): Promise<Operador | null> {
    const operador = await this.findOne(id, tenantId);
    if (!operador) return null;

    operador.estado = nuevoEstado;
    operador.updatedById = updatedBy;

    return this.operadorRepository.save(operador);
  }

  // Operadores disponibles
  async getOperadoresDisponibles(tenantId: string): Promise<Operador[]> {
    return this.operadorRepository.find({
      where: {
        tenantId,
        estado: EstadoOperador.DISPONIBLE,
      },
      order: { apellidoPaterno: 'ASC', nombre: 'ASC' },
    });
  }

  // Operadores con licencia por vencer
  async getOperadoresLicenciaPorVencer(tenantId: string, diasAntelacion: number = 30): Promise<Operador[]> {
    const fechaLimite = new Date();
    fechaLimite.setDate(fechaLimite.getDate() + diasAntelacion);

    const operadores = await this.operadorRepository
      .createQueryBuilder('o')
      .where('o.tenant_id = :tenantId', { tenantId })
      .andWhere('o.vigencia_licencia <= :fechaLimite', { fechaLimite })
      .andWhere('o.vigencia_licencia > :hoy', { hoy: new Date() })
      .orderBy('o.vigencia_licencia', 'ASC')
      .getMany();

    return operadores;
  }

  // Operadores con licencia vencida
  async getOperadoresLicenciaVencida(tenantId: string): Promise<Operador[]> {
    const hoy = new Date();

    const operadores = await this.operadorRepository
      .createQueryBuilder('o')
      .where('o.tenant_id = :tenantId', { tenantId })
      .andWhere('o.vigencia_licencia < :hoy', { hoy })
      .orderBy('o.vigencia_licencia', 'ASC')
      .getMany();

    return operadores;
  }

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

    if (operador.estado === EstadoOperador.EN_RUTA) {
      throw new Error('No se puede eliminar un operador en ruta');
    }

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