import { Repository, FindOptionsWhere, ILike, Between, In } from 'typeorm';
import { AppDataSource } from '../../../config/typeorm.js';
import { CargaCombustible, TipoCargaCombustible, EstadoGasto } from '../entities/carga-combustible.entity.js';

/**
 * Search parameters for fuel loads
 */
export interface CargaCombustibleSearchParams {
  tenantId: string;
  search?: string;
  unidadId?: string;
  viajeId?: string;
  operadorId?: string;
  tipoCarga?: TipoCargaCombustible;
  estado?: EstadoGasto;
  estados?: EstadoGasto[];
  fechaDesde?: Date;
  fechaHasta?: Date;
  limit?: number;
  offset?: number;
}

/**
 * DTO for creating a fuel load
 */
export interface CreateCargaCombustibleDto {
  unidadId: string;
  viajeId?: string;
  operadorId: string;
  tipoCarga: TipoCargaCombustible;
  tipoCombustible: string;
  litros: number;
  precioLitro: number;
  total: number;
  odometroCarga?: number;
  estacionId?: string;
  estacionNombre?: string;
  estacionDireccion?: string;
  latitud?: number;
  longitud?: number;
  numeroVale?: string;
  numeroFactura?: string;
  folioTicket?: string;
  fechaCarga: Date;
  fotoTicketUrl?: string;
}

/**
 * DTO for updating a fuel load
 */
export interface UpdateCargaCombustibleDto extends Partial<CreateCargaCombustibleDto> {
  estado?: EstadoGasto;
  rendimientoCalculado?: number;
}

/**
 * Service for managing fuel loads (Cargas de Combustible)
 */
export class CargaCombustibleService {
  private repository: Repository<CargaCombustible>;

  constructor() {
    this.repository = AppDataSource.getRepository(CargaCombustible);
  }

  /**
   * Find all fuel loads with filters
   */
  async findAll(params: CargaCombustibleSearchParams): Promise<{ data: CargaCombustible[]; total: number }> {
    const {
      tenantId,
      search,
      unidadId,
      viajeId,
      operadorId,
      tipoCarga,
      estado,
      estados,
      fechaDesde,
      fechaHasta,
      limit = 50,
      offset = 0,
    } = params;

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

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

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

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

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

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

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

    if (fechaDesde && fechaHasta) {
      baseWhere.fechaCarga = Between(fechaDesde, fechaHasta);
    }

    if (search) {
      where.push(
        { ...baseWhere, estacionNombre: ILike(`%${search}%`) },
        { ...baseWhere, numeroVale: ILike(`%${search}%`) },
        { ...baseWhere, folioTicket: ILike(`%${search}%`) }
      );
    } else {
      where.push(baseWhere);
    }

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

    return { data, total };
  }

  /**
   * Find a single fuel load by ID
   */
  async findOne(tenantId: string, id: string): Promise<CargaCombustible | null> {
    return this.repository.findOne({
      where: { id, tenantId },
    });
  }

  /**
   * Create a new fuel load
   */
  async create(tenantId: string, dto: CreateCargaCombustibleDto, createdBy: string): Promise<CargaCombustible> {
    const carga = this.repository.create({
      ...dto,
      tenantId,
      estado: EstadoGasto.PENDIENTE,
      createdById: createdBy,
    });

    return this.repository.save(carga);
  }

  /**
   * Update an existing fuel load
   */
  async update(
    tenantId: string,
    id: string,
    dto: UpdateCargaCombustibleDto
  ): Promise<CargaCombustible | null> {
    const carga = await this.findOne(tenantId, id);
    if (!carga) return null;

    Object.assign(carga, dto);
    return this.repository.save(carga);
  }

  /**
   * Approve a fuel load
   */
  async aprobar(tenantId: string, id: string, aprobadoPor: string): Promise<CargaCombustible | null> {
    const carga = await this.findOne(tenantId, id);
    if (!carga) return null;

    if (carga.estado !== EstadoGasto.PENDIENTE) {
      throw new Error('Solo se pueden aprobar cargas pendientes');
    }

    carga.estado = EstadoGasto.APROBADO;
    carga.aprobadoPor = aprobadoPor;
    carga.aprobadoFecha = new Date();

    return this.repository.save(carga);
  }

  /**
   * Reject a fuel load
   */
  async rechazar(tenantId: string, id: string, aprobadoPor: string): Promise<CargaCombustible | null> {
    const carga = await this.findOne(tenantId, id);
    if (!carga) return null;

    if (carga.estado !== EstadoGasto.PENDIENTE) {
      throw new Error('Solo se pueden rechazar cargas pendientes');
    }

    carga.estado = EstadoGasto.RECHAZADO;
    carga.aprobadoPor = aprobadoPor;
    carga.aprobadoFecha = new Date();

    return this.repository.save(carga);
  }

  /**
   * Delete a fuel load (soft or hard)
   */
  async delete(tenantId: string, id: string): Promise<boolean> {
    const carga = await this.findOne(tenantId, id);
    if (!carga) return false;

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

  /**
   * Get fuel loads by unit
   */
  async getCargasPorUnidad(unidadId: string, tenantId: string): Promise<CargaCombustible[]> {
    return this.repository.find({
      where: { unidadId, tenantId },
      order: { fechaCarga: 'DESC' },
      take: 50,
    });
  }

  /**
   * Get fuel loads by trip
   */
  async getCargasPorViaje(viajeId: string, tenantId: string): Promise<CargaCombustible[]> {
    return this.repository.find({
      where: { viajeId, tenantId },
      order: { fechaCarga: 'ASC' },
    });
  }

  /**
   * Get pending fuel loads
   */
  async getCargasPendientes(tenantId: string): Promise<CargaCombustible[]> {
    return this.repository.find({
      where: { tenantId, estado: EstadoGasto.PENDIENTE },
      order: { fechaCarga: 'DESC' },
    });
  }

  /**
   * Calculate fuel performance for a unit in a period
   */
  async calcularRendimiento(
    unidadId: string,
    tenantId: string,
    fechaInicio: Date,
    fechaFin: Date
  ): Promise<{ totalLitros: number; totalKm: number; rendimiento: number }> {
    const cargas = await this.repository.find({
      where: {
        unidadId,
        tenantId,
        estado: EstadoGasto.APROBADO,
        fechaCarga: Between(fechaInicio, fechaFin),
      },
      order: { fechaCarga: 'ASC' },
    });

    if (cargas.length < 2) {
      return { totalLitros: 0, totalKm: 0, rendimiento: 0 };
    }

    let totalLitros = 0;
    let kmInicial = cargas[0].odometroCarga || 0;
    let kmFinal = kmInicial;

    for (const carga of cargas) {
      totalLitros += Number(carga.litros);
      if (carga.odometroCarga) {
        kmFinal = carga.odometroCarga;
      }
    }

    const totalKm = kmFinal - kmInicial;
    const rendimiento = totalLitros > 0 ? totalKm / totalLitros : 0;

    return { totalLitros, totalKm, rendimiento };
  }
}

export const cargaCombustibleService = new CargaCombustibleService();
