/**
 * @ai-summary Enhanced business logic for fuel logs feature
 * @ai-context Unit-agnostic efficiency and user preferences integration
 */

import { FuelLogsRepository } from '../data/fuel-logs.repository';
import { EnhancedCreateFuelLogRequest, EnhancedUpdateFuelLogRequest, EnhancedFuelLogResponse, FuelType } from './fuel-logs.types';
import { logger } from '../../../core/logging/logger';
import { cacheService } from '../../../core/config/redis';
import pool from '../../../core/config/database';
import { EnhancedValidationService } from './enhanced-validation.service';
import { UnitConversionService } from './unit-conversion.service';
import { EfficiencyCalculationService } from './efficiency-calculation.service';
import { UserSettingsService } from '../external/user-settings.service';

export class FuelLogsService {
  private readonly cachePrefix = 'fuel-logs';
  private readonly cacheTTL = 300; // 5 minutes

  constructor(private repository: FuelLogsRepository) {}

  async createFuelLog(data: EnhancedCreateFuelLogRequest, userId: string): Promise<EnhancedFuelLogResponse> {
    logger.info('Creating enhanced fuel log', { userId, vehicleId: data.vehicleId, fuelType: data.fuelType });
    const userSettings = await UserSettingsService.getUserSettings(userId);

    const validation = EnhancedValidationService.validateFuelLogData(data);
    if (!validation.isValid) {
      throw new Error(validation.errors.join(', '));
    }

    // Verify vehicle ownership
    const vehicleCheck = await pool.query(
      'SELECT id FROM vehicles WHERE id = $1 AND user_id = $2',
      [data.vehicleId, userId]
    );
    if (vehicleCheck.rows.length === 0) throw new Error('Vehicle not found or unauthorized');

    const totalCost = data.fuelUnits * data.costPerUnit;

    // Previous log for efficiency
    const prev = data.odometerReading
      ? await this.repository.getPreviousLogByOdometer(data.vehicleId, data.odometerReading)
      : await this.repository.getLatestLogForVehicle(data.vehicleId);

    const eff = EfficiencyCalculationService.calculateEfficiency(
      { odometerReading: data.odometerReading, tripDistance: data.tripDistance, fuelUnits: data.fuelUnits },
      prev?.odometer ?? null,
      userSettings.unitSystem
    );

    const inserted = await this.repository.createEnhanced({
      userId,
      vehicleId: data.vehicleId,
      dateTime: new Date(data.dateTime),
      odometerReading: data.odometerReading,
      tripDistance: data.tripDistance,
      fuelType: data.fuelType,
      fuelGrade: data.fuelGrade ?? null,
      fuelUnits: data.fuelUnits,
      costPerUnit: data.costPerUnit,
      totalCost,
      locationData: data.locationData ?? null,
      notes: data.notes
    });

    if (data.odometerReading) {
      await pool.query(
        'UPDATE vehicles SET odometer_reading = $1 WHERE id = $2 AND (odometer_reading IS NULL OR odometer_reading < $1)',
        [data.odometerReading, data.vehicleId]
      );
    }

    await this.invalidateCaches(userId, data.vehicleId, userSettings.unitSystem);
    return this.toEnhancedResponse(inserted, eff?.value ?? undefined, userSettings.unitSystem);
  }

  async getFuelLogsByVehicle(vehicleId: string, userId: string): Promise<EnhancedFuelLogResponse[]> {
    const vehicleCheck = await pool.query('SELECT id FROM vehicles WHERE id = $1 AND user_id = $2', [vehicleId, userId]);
    if (vehicleCheck.rows.length === 0) throw new Error('Vehicle not found or unauthorized');

    const { unitSystem } = await UserSettingsService.getUserSettings(userId);
    const cacheKey = `${this.cachePrefix}:vehicle:${vehicleId}:${unitSystem}`;

    const cached = await cacheService.get<EnhancedFuelLogResponse[]>(cacheKey);
    if (cached) return cached;

    const rows = await this.repository.findByVehicleIdEnhanced(vehicleId);
    const response = rows.map(r => {
      const efficiency = EfficiencyCalculationService.calculateEfficiency(
        {
          tripDistance: r.trip_distance ?? undefined,
          fuelUnits: r.fuel_units ?? undefined
        },
        null, // No previous odometer needed for trip distance calculation
        unitSystem
      );
      return this.toEnhancedResponse(r, efficiency?.value ?? undefined, unitSystem);
    });
    await cacheService.set(cacheKey, response, this.cacheTTL);
    return response;
  }

  async getUserFuelLogs(userId: string): Promise<EnhancedFuelLogResponse[]> {
    const { unitSystem } = await UserSettingsService.getUserSettings(userId);
    const cacheKey = `${this.cachePrefix}:user:${userId}:${unitSystem}`;
    const cached = await cacheService.get<EnhancedFuelLogResponse[]>(cacheKey);
    if (cached) return cached;
    const rows = await this.repository.findByUserIdEnhanced(userId);
    const response = rows.map(r => {
      const efficiency = EfficiencyCalculationService.calculateEfficiency(
        {
          tripDistance: r.trip_distance ?? undefined,
          fuelUnits: r.fuel_units ?? undefined
        },
        null, // No previous odometer needed for trip distance calculation
        unitSystem
      );
      return this.toEnhancedResponse(r, efficiency?.value ?? undefined, unitSystem);
    });
    await cacheService.set(cacheKey, response, this.cacheTTL);
    return response;
  }

  async getFuelLog(id: string, userId: string): Promise<EnhancedFuelLogResponse> {
    const row = await this.repository.findByIdEnhanced(id);
    if (!row) throw new Error('Fuel log not found');
    if (row.user_id !== userId) throw new Error('Unauthorized');
    const { unitSystem } = await UserSettingsService.getUserSettings(userId);

    const efficiency = EfficiencyCalculationService.calculateEfficiency(
      {
        tripDistance: row.trip_distance ?? undefined,
        fuelUnits: row.fuel_units ?? undefined
      },
      null, // No previous odometer needed for trip distance calculation
      unitSystem
    );

    return this.toEnhancedResponse(row, efficiency?.value ?? undefined, unitSystem);
  }

  async updateFuelLog(id: string, data: EnhancedUpdateFuelLogRequest, userId: string): Promise<EnhancedFuelLogResponse> {
    logger.info('Updating enhanced fuel log', { id, userId });

    // Verify the fuel log exists and belongs to the user
    const existing = await this.repository.findByIdEnhanced(id);
    if (!existing) throw new Error('Fuel log not found');
    if (existing.user_id !== userId) throw new Error('Unauthorized');

    // Get user settings for unit conversion
    const userSettings = await UserSettingsService.getUserSettings(userId);

    // Validate the update data
    if (Object.keys(data).length === 0) {
      throw new Error('No fields provided for update');
    }

    // Prepare update data with proper type conversion
    const updateData: any = {};

    if (data.dateTime !== undefined) {
      updateData.dateTime = new Date(data.dateTime);
    }
    if (data.odometerReading !== undefined) {
      updateData.odometerReading = data.odometerReading;
    }
    if (data.tripDistance !== undefined) {
      updateData.tripDistance = data.tripDistance;
    }
    if (data.fuelType !== undefined) {
      updateData.fuelType = data.fuelType;
    }
    if (data.fuelGrade !== undefined) {
      updateData.fuelGrade = data.fuelGrade;
    }
    if (data.fuelUnits !== undefined) {
      updateData.fuelUnits = data.fuelUnits;
    }
    if (data.costPerUnit !== undefined) {
      updateData.costPerUnit = data.costPerUnit;
    }
    if (data.locationData !== undefined) {
      updateData.locationData = data.locationData;
    }
    if (data.notes !== undefined) {
      updateData.notes = data.notes;
    }

    // Update the fuel log
    const updated = await this.repository.updateEnhanced(id, updateData);
    if (!updated) throw new Error('Failed to update fuel log');

    // Update vehicle odometer if changed
    if (data.odometerReading !== undefined) {
      await pool.query(
        'UPDATE vehicles SET odometer_reading = $1 WHERE id = $2 AND user_id = $3 AND (odometer_reading IS NULL OR odometer_reading < $1)',
        [data.odometerReading, existing.vehicle_id, userId]
      );
    }

    // Invalidate caches
    await this.invalidateCaches(userId, existing.vehicle_id, userSettings.unitSystem);

    // Calculate efficiency for response
    const efficiency = EfficiencyCalculationService.calculateEfficiency(
      {
        odometerReading: updated.odometer ?? undefined,
        tripDistance: updated.trip_distance ?? undefined,
        fuelUnits: updated.fuel_units ?? undefined
      },
      null, // Previous log efficiency calculation would require more complex logic for updates
      userSettings.unitSystem
    );

    return this.toEnhancedResponse(updated, efficiency?.value ?? undefined, userSettings.unitSystem);
  }

  async deleteFuelLog(id: string, userId: string): Promise<void> {
    const existing = await this.repository.findByIdEnhanced(id);
    if (!existing) throw new Error('Fuel log not found');
    if (existing.user_id !== userId) throw new Error('Unauthorized');
    await this.repository.delete(id);
    await this.invalidateCaches(userId, existing.vehicle_id, 'imperial'); // cache keys include unit; simple sweep below
  }

  async getVehicleStats(vehicleId: string, userId: string): Promise<any> {
    const vehicleCheck = await pool.query('SELECT id FROM vehicles WHERE id = $1 AND user_id = $2', [vehicleId, userId]);
    if (vehicleCheck.rows.length === 0) throw new Error('Vehicle not found or unauthorized');

    const { unitSystem } = await UserSettingsService.getUserSettings(userId);
    const rows = await this.repository.findByVehicleIdEnhanced(vehicleId);
    const labels = UnitConversionService.getUnitLabels(unitSystem);
    if (rows.length === 0) {
      return { logCount: 0, totalFuelUnits: 0, totalCost: 0, averageCostPerUnit: 0, totalDistance: 0, averageEfficiency: 0, unitLabels: labels };
    }

    const totalFuelUnits = rows.reduce((s, r) => s + (Number(r.fuel_units) || 0), 0);
    const totalCost = rows.reduce((s, r) => s + (Number(r.total_cost) || 0), 0);
    const averageCostPerUnit = totalFuelUnits > 0 ? totalCost / totalFuelUnits : 0;

    const sorted = [...rows].sort((a, b) => (new Date(b.date_time || b.date)).getTime() - (new Date(a.date_time || a.date)).getTime());
    let totalDistance = 0;
    for (let i = 0; i < sorted.length; i++) {
      const cur = sorted[i];
      const prev = sorted[i + 1];
      if (Number(cur.trip_distance) > 0) totalDistance += Number(cur.trip_distance);
      else if (prev && cur.odometer != null && prev.odometer != null) {
        const d = Number(cur.odometer) - Number(prev.odometer);
        if (d > 0) totalDistance += d;
      }
    }

    const efficiencies: number[] = sorted.map(l => {
      const e = EfficiencyCalculationService.calculateEfficiency(
        { odometerReading: l.odometer ?? undefined, tripDistance: l.trip_distance ?? undefined, fuelUnits: l.fuel_units ?? undefined },
        null,
        unitSystem
      );
      return e?.value || 0;
    }).filter(v => v > 0);
    const averageEfficiency = efficiencies.length ? (efficiencies.reduce((a, b) => a + b, 0) / efficiencies.length) : 0;

    return { logCount: rows.length, totalFuelUnits, totalCost, averageCostPerUnit, totalDistance, averageEfficiency, unitLabels: labels };
  }

  private async invalidateCaches(userId: string, vehicleId: string, unitSystem: 'imperial' | 'metric'): Promise<void> {
    await Promise.all([
      cacheService.del(`${this.cachePrefix}:user:${userId}:${unitSystem}`),
      cacheService.del(`${this.cachePrefix}:vehicle:${vehicleId}:${unitSystem}`)
    ]);
  }

  private toEnhancedResponse(row: any, efficiency: number | undefined, unitSystem: 'imperial' | 'metric'): EnhancedFuelLogResponse {
    const labels = UnitConversionService.getUnitLabels(unitSystem);
    const dateTime = row.date_time ? new Date(row.date_time) : (row.date ? new Date(row.date) : new Date());
    return {
      id: row.id,
      userId: row.user_id,
      vehicleId: row.vehicle_id,
      dateTime: dateTime.toISOString(),
      odometerReading: row.odometer ?? undefined,
      tripDistance: row.trip_distance ?? undefined,
      fuelType: row.fuel_type as FuelType,
      fuelGrade: row.fuel_grade ?? undefined,
      fuelUnits: row.fuel_units,
      costPerUnit: row.cost_per_unit,
      totalCost: Number(row.total_cost),
      locationData: row.location_data ?? undefined,
      notes: row.notes ?? undefined,
      efficiency: efficiency,
      efficiencyLabel: labels.efficiencyUnits,
      createdAt: new Date(row.created_at).toISOString(),
      updatedAt: new Date(row.updated_at).toISOString(),
    };
  }
}
