import { Repository, FindOptionsWhere, Between, In } from 'typeorm';
import { AppDataSource } from '../../../config/typeorm.js';
import { ControlRendimiento, TipoAnomaliaRendimiento } from '../entities/control-rendimiento.entity.js';

/**
 * Search parameters for fuel performance controls
 */
export interface ControlRendimientoSearchParams {
  tenantId: string;
  unidadId?: string;
  tieneAnomalia?: boolean;
  tipoAnomalia?: string;
  fechaDesde?: Date;
  fechaHasta?: Date;
  limit?: number;
  offset?: number;
}

/**
 * DTO for creating a fuel performance control
 */
export interface CreateControlRendimientoDto {
  unidadId: string;
  fechaInicio: Date;
  fechaFin: Date;
  kmRecorridos: number;
  litrosConsumidos: number;
  rendimientoReal: number;
  rendimientoEsperado?: number;
  variacionPorcentaje?: number;
  costoTotalCombustible?: number;
  costoPorKm?: number;
  tieneAnomalia?: boolean;
  tipoAnomalia?: string;
  descripcionAnomalia?: string;
}

/**
 * DTO for updating a fuel performance control
 */
export interface UpdateControlRendimientoDto extends Partial<CreateControlRendimientoDto> {}

/**
 * Service for managing fuel performance controls (Control de Rendimiento)
 */
export class ControlRendimientoService {
  private repository: Repository<ControlRendimiento>;

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

  /**
   * Find all performance controls with filters
   */
  async findAll(params: ControlRendimientoSearchParams): Promise<{ data: ControlRendimiento[]; total: number }> {
    const {
      tenantId,
      unidadId,
      tieneAnomalia,
      tipoAnomalia,
      fechaDesde,
      fechaHasta,
      limit = 50,
      offset = 0,
    } = params;

    const where: FindOptionsWhere<ControlRendimiento> = { tenantId };

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

    if (tieneAnomalia !== undefined) {
      where.tieneAnomalia = tieneAnomalia;
    }

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

    if (fechaDesde && fechaHasta) {
      where.fechaInicio = Between(fechaDesde, fechaHasta);
    }

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

    return { data, total };
  }

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

  /**
   * Create a new performance control
   */
  async create(tenantId: string, dto: CreateControlRendimientoDto): Promise<ControlRendimiento> {
    // Calculate variation percentage if not provided
    if (!dto.variacionPorcentaje && dto.rendimientoEsperado && dto.rendimientoEsperado > 0) {
      dto.variacionPorcentaje = ((dto.rendimientoReal - dto.rendimientoEsperado) / dto.rendimientoEsperado) * 100;
    }

    // Calculate cost per km if not provided
    if (!dto.costoPorKm && dto.costoTotalCombustible && dto.kmRecorridos > 0) {
      dto.costoPorKm = dto.costoTotalCombustible / dto.kmRecorridos;
    }

    // Detect anomaly if not explicitly set
    if (dto.tieneAnomalia === undefined && dto.variacionPorcentaje !== undefined) {
      dto.tieneAnomalia = dto.variacionPorcentaje < -15; // 15% below expected is an anomaly
      if (dto.tieneAnomalia && !dto.tipoAnomalia) {
        dto.tipoAnomalia = TipoAnomaliaRendimiento.BAJO_RENDIMIENTO;
      }
    }

    const control = this.repository.create({
      ...dto,
      tenantId,
    });

    return this.repository.save(control);
  }

  /**
   * Update an existing performance control
   */
  async update(
    tenantId: string,
    id: string,
    dto: UpdateControlRendimientoDto
  ): Promise<ControlRendimiento | null> {
    const control = await this.findOne(tenantId, id);
    if (!control) return null;

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

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

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

  /**
   * Get performance controls by unit
   */
  async getControlesPorUnidad(unidadId: string, tenantId: string): Promise<ControlRendimiento[]> {
    return this.repository.find({
      where: { unidadId, tenantId },
      order: { fechaInicio: 'DESC' },
      take: 20,
    });
  }

  /**
   * Get controls with anomalies
   */
  async getControlesConAnomalias(tenantId: string): Promise<ControlRendimiento[]> {
    return this.repository.find({
      where: { tenantId, tieneAnomalia: true },
      order: { fechaInicio: 'DESC' },
    });
  }

  /**
   * Get controls by anomaly type
   */
  async getControlesPorTipoAnomalia(
    tipoAnomalia: string,
    tenantId: string
  ): Promise<ControlRendimiento[]> {
    return this.repository.find({
      where: { tenantId, tipoAnomalia },
      order: { fechaInicio: 'DESC' },
    });
  }

  /**
   * Generate performance control for a unit in a period
   */
  async generarControlPeriodo(
    unidadId: string,
    tenantId: string,
    fechaInicio: Date,
    fechaFin: Date,
    rendimientoEsperado: number,
    kmRecorridos: number,
    litrosConsumidos: number,
    costoTotal: number
  ): Promise<ControlRendimiento> {
    const rendimientoReal = litrosConsumidos > 0 ? kmRecorridos / litrosConsumidos : 0;
    const variacionPorcentaje = rendimientoEsperado > 0
      ? ((rendimientoReal - rendimientoEsperado) / rendimientoEsperado) * 100
      : 0;

    let tieneAnomalia = false;
    let tipoAnomalia: string | undefined;
    let descripcionAnomalia: string | undefined;

    // Detect anomalies based on variation
    if (variacionPorcentaje < -25) {
      tieneAnomalia = true;
      tipoAnomalia = TipoAnomaliaRendimiento.CONSUMO_EXCESIVO;
      descripcionAnomalia = `Consumo excesivo: ${Math.abs(variacionPorcentaje).toFixed(1)}% por encima del esperado`;
    } else if (variacionPorcentaje < -15) {
      tieneAnomalia = true;
      tipoAnomalia = TipoAnomaliaRendimiento.BAJO_RENDIMIENTO;
      descripcionAnomalia = `Bajo rendimiento: ${Math.abs(variacionPorcentaje).toFixed(1)}% por debajo del esperado`;
    }

    return this.create(tenantId, {
      unidadId,
      fechaInicio,
      fechaFin,
      kmRecorridos,
      litrosConsumidos,
      rendimientoReal,
      rendimientoEsperado,
      variacionPorcentaje,
      costoTotalCombustible: costoTotal,
      costoPorKm: kmRecorridos > 0 ? costoTotal / kmRecorridos : 0,
      tieneAnomalia,
      tipoAnomalia,
      descripcionAnomalia,
    });
  }

  /**
   * Get fleet performance statistics
   */
  async getEstadisticasFlota(
    tenantId: string,
    fechaInicio: Date,
    fechaFin: Date
  ): Promise<{
    promedioRendimiento: number;
    totalKmRecorridos: number;
    totalLitrosConsumidos: number;
    costoTotalCombustible: number;
    costoPorKmPromedio: number;
    cantidadUnidades: number;
    unidadesConAnomalia: number;
  }> {
    const controles = await this.repository.find({
      where: {
        tenantId,
        fechaInicio: Between(fechaInicio, fechaFin),
      },
    });

    if (controles.length === 0) {
      return {
        promedioRendimiento: 0,
        totalKmRecorridos: 0,
        totalLitrosConsumidos: 0,
        costoTotalCombustible: 0,
        costoPorKmPromedio: 0,
        cantidadUnidades: 0,
        unidadesConAnomalia: 0,
      };
    }

    const totalKmRecorridos = controles.reduce((sum, c) => sum + c.kmRecorridos, 0);
    const totalLitrosConsumidos = controles.reduce((sum, c) => sum + Number(c.litrosConsumidos), 0);
    const costoTotalCombustible = controles.reduce((sum, c) => sum + (Number(c.costoTotalCombustible) || 0), 0);
    const unidadesUnicas = new Set(controles.map(c => c.unidadId));
    const unidadesConAnomalia = new Set(controles.filter(c => c.tieneAnomalia).map(c => c.unidadId));

    return {
      promedioRendimiento: totalLitrosConsumidos > 0 ? totalKmRecorridos / totalLitrosConsumidos : 0,
      totalKmRecorridos,
      totalLitrosConsumidos,
      costoTotalCombustible,
      costoPorKmPromedio: totalKmRecorridos > 0 ? costoTotalCombustible / totalKmRecorridos : 0,
      cantidadUnidades: unidadesUnicas.size,
      unidadesConAnomalia: unidadesConAnomalia.size,
    };
  }

  /**
   * Get unit ranking by performance
   */
  async getRankingRendimiento(
    tenantId: string,
    fechaInicio: Date,
    fechaFin: Date,
    limit: number = 10
  ): Promise<Array<{ unidadId: string; rendimientoPromedio: number; kmTotales: number }>> {
    const controles = await this.repository.find({
      where: {
        tenantId,
        fechaInicio: Between(fechaInicio, fechaFin),
      },
    });

    // Group by unit
    const porUnidad = new Map<string, { sumRendimiento: number; sumKm: number; count: number }>();

    for (const control of controles) {
      const current = porUnidad.get(control.unidadId) || { sumRendimiento: 0, sumKm: 0, count: 0 };
      current.sumRendimiento += Number(control.rendimientoReal);
      current.sumKm += control.kmRecorridos;
      current.count++;
      porUnidad.set(control.unidadId, current);
    }

    // Calculate averages and sort
    const ranking = Array.from(porUnidad.entries())
      .map(([unidadId, data]) => ({
        unidadId,
        rendimientoPromedio: data.count > 0 ? data.sumRendimiento / data.count : 0,
        kmTotales: data.sumKm,
      }))
      .sort((a, b) => b.rendimientoPromedio - a.rendimientoPromedio)
      .slice(0, limit);

    return ranking;
  }

  /**
   * Mark anomaly as investigated
   */
  async marcarAnomaliaInvestigada(
    tenantId: string,
    id: string,
    tipoAnomaliaConfirmado: string,
    descripcion: string
  ): Promise<ControlRendimiento | null> {
    const control = await this.findOne(tenantId, id);
    if (!control) return null;

    control.tipoAnomalia = tipoAnomaliaConfirmado;
    control.descripcionAnomalia = descripcion;

    return this.repository.save(control);
  }
}

export const controlRendimientoService = new ControlRendimientoService();
