import { Repository, FindOptionsWhere, ILike, In } from 'typeorm';
import { Unidad, TipoUnidad, EstadoUnidad } from '../entities';

export interface UnidadSearchParams {
  tenantId: string;
  search?: string;
  tipo?: TipoUnidad;
  estado?: EstadoUnidad;
  estados?: EstadoUnidad[];
  sucursalId?: string;
  limit?: number;
  offset?: number;
}

export interface CreateUnidadDto {
  numeroEconomico: string;
  tipo: TipoUnidad;
  marca: string;
  modelo: string;
  anio: number;
  placas: string;
  numeroSerie?: string;
  numeroMotor?: string;
  color?: string;
  capacidadCarga?: number;
  pesoNeto?: number;
  pesoBrutoVehicular?: number;
  numEjes?: number;
  numLlantas?: number;
  tipoCarroceria?: string;
  sucursalId?: string;
  permisoSct?: string;
  numPermisoSct?: string;
  configVehicular?: string;
  polizaSeguro?: string;
  aseguradora?: string;
  vigenciaSeguroInicio?: Date;
  vigenciaSeguroFin?: Date;
  verificacionVigencia?: Date;
  odometroActual?: number;
  rendimientoEsperado?: number;
}

export interface UpdateUnidadDto extends Partial<CreateUnidadDto> {
  estado?: EstadoUnidad;
}

export class UnidadesService {
  constructor(private readonly unidadRepository: Repository<Unidad>) {}

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

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

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

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

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

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

    if (search) {
      where.push(
        { ...baseWhere, numeroEconomico: ILike(`%${search}%`) },
        { ...baseWhere, placas: ILike(`%${search}%`) },
        { ...baseWhere, marca: ILike(`%${search}%`) },
        { ...baseWhere, modelo: ILike(`%${search}%`) }
      );
    } else {
      where.push(baseWhere);
    }

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

    return { data, total };
  }

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

  async findByNumeroEconomico(numeroEconomico: string, tenantId: string): Promise<Unidad | null> {
    return this.unidadRepository.findOne({
      where: { numeroEconomico, tenantId },
    });
  }

  async findByPlacas(placas: string, tenantId: string): Promise<Unidad | null> {
    return this.unidadRepository.findOne({
      where: { placas, tenantId },
    });
  }

  async create(tenantId: string, dto: CreateUnidadDto, createdBy: string): Promise<Unidad> {
    // Check for existing numero económico
    const existingNumero = await this.findByNumeroEconomico(dto.numeroEconomico, tenantId);
    if (existingNumero) {
      throw new Error('Ya existe una unidad con este número económico');
    }

    // Check for existing placas
    const existingPlacas = await this.findByPlacas(dto.placas, tenantId);
    if (existingPlacas) {
      throw new Error('Ya existe una unidad con estas placas');
    }

    const unidad = this.unidadRepository.create({
      ...dto,
      tenantId,
      estado: EstadoUnidad.DISPONIBLE,
      createdById: createdBy,
    });

    return this.unidadRepository.save(unidad);
  }

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

    // If changing numero económico, check for duplicates
    if (dto.numeroEconomico && dto.numeroEconomico !== unidad.numeroEconomico) {
      const existing = await this.findByNumeroEconomico(dto.numeroEconomico, tenantId);
      if (existing) {
        throw new Error('Ya existe una unidad con este número económico');
      }
    }

    // If changing placas, check for duplicates
    if (dto.placas && dto.placas !== unidad.placas) {
      const existing = await this.findByPlacas(dto.placas, tenantId);
      if (existing && existing.id !== id) {
        throw new Error('Ya existe una unidad con estas placas');
      }
    }

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

    return this.unidadRepository.save(unidad);
  }

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

    unidad.estado = nuevoEstado;
    unidad.updatedById = updatedBy;

    return this.unidadRepository.save(unidad);
  }

  async actualizarOdometro(
    id: string,
    tenantId: string,
    odometro: number,
    updatedBy: string
  ): Promise<Unidad | null> {
    const unidad = await this.findOne(id, tenantId);
    if (!unidad) return null;

    if (odometro < (unidad.odometroActual || 0)) {
      throw new Error('El odómetro no puede ser menor al actual');
    }

    unidad.odometroActual = odometro;
    unidad.updatedById = updatedBy;

    return this.unidadRepository.save(unidad);
  }

  // Unidades disponibles
  async getUnidadesDisponibles(tenantId: string, tipo?: TipoUnidad): Promise<Unidad[]> {
    const where: FindOptionsWhere<Unidad> = {
      tenantId,
      estado: EstadoUnidad.DISPONIBLE,
    };

    if (tipo) {
      where.tipo = tipo;
    }

    return this.unidadRepository.find({
      where,
      order: { numeroEconomico: 'ASC' },
    });
  }

  // Unidades con seguro por vencer
  async getUnidadesSeguroPorVencer(tenantId: string, diasAntelacion: number = 30): Promise<Unidad[]> {
    const fechaLimite = new Date();
    fechaLimite.setDate(fechaLimite.getDate() + diasAntelacion);

    const unidades = await this.unidadRepository
      .createQueryBuilder('u')
      .where('u.tenant_id = :tenantId', { tenantId })
      .andWhere('u.vigencia_seguro_fin <= :fechaLimite', { fechaLimite })
      .andWhere('u.vigencia_seguro_fin > :hoy', { hoy: new Date() })
      .orderBy('u.vigencia_seguro_fin', 'ASC')
      .getMany();

    return unidades;
  }

  // Unidades con verificación por vencer
  async getUnidadesVerificacionPorVencer(tenantId: string, diasAntelacion: number = 30): Promise<Unidad[]> {
    const fechaLimite = new Date();
    fechaLimite.setDate(fechaLimite.getDate() + diasAntelacion);

    const unidades = await this.unidadRepository
      .createQueryBuilder('u')
      .where('u.tenant_id = :tenantId', { tenantId })
      .andWhere('u.verificacion_vigencia <= :fechaLimite', { fechaLimite })
      .andWhere('u.verificacion_vigencia > :hoy', { hoy: new Date() })
      .orderBy('u.verificacion_vigencia', 'ASC')
      .getMany();

    return unidades;
  }

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

    if (unidad.estado === EstadoUnidad.EN_RUTA) {
      throw new Error('No se puede eliminar una unidad en ruta');
    }

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