/**
 * @ai-summary Data access layer for fuel logs
 * @ai-context Handles database operations and MPG calculations
 */

import { Pool } from 'pg';
import { FuelLog, CreateFuelLogRequest, FuelStats } from '../domain/fuel-logs.types';

export class FuelLogsRepository {
  constructor(private pool: Pool) {}

  async create(data: CreateFuelLogRequest & { userId: string, mpg?: number }): Promise<FuelLog> {
    const query = `
      INSERT INTO fuel_logs (
        user_id, vehicle_id, date, odometer, gallons,
        price_per_gallon, total_cost, station, location, notes
      )
      VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
      RETURNING *
    `;
    
    const values = [
      data.userId,
      data.vehicleId,
      data.date,
      data.odometer,
      data.gallons,
      data.pricePerGallon,
      data.totalCost,
      data.station,
      data.location,
      data.notes
    ];

    const result = await this.pool.query(query, values);
    return this.mapRow(result.rows[0]);
  }

  async findByVehicleId(vehicleId: string): Promise<FuelLog[]> {
    const query = `
      SELECT * FROM fuel_logs
      WHERE vehicle_id = $1
      ORDER BY date DESC, created_at DESC
    `;
    
    const result = await this.pool.query(query, [vehicleId]);
    return result.rows.map(row => this.mapRow(row));
  }

  async findByUserId(userId: string): Promise<FuelLog[]> {
    const query = `
      SELECT * FROM fuel_logs
      WHERE user_id = $1
      ORDER BY date DESC, created_at DESC
    `;
    
    const result = await this.pool.query(query, [userId]);
    return result.rows.map(row => this.mapRow(row));
  }

  async findById(id: string): Promise<FuelLog | null> {
    const query = 'SELECT * FROM fuel_logs WHERE id = $1';
    const result = await this.pool.query(query, [id]);
    
    if (result.rows.length === 0) {
      return null;
    }
    
    return this.mapRow(result.rows[0]);
  }

  async getPreviousLog(vehicleId: string, date: string, odometer: number): Promise<FuelLog | null> {
    const query = `
      SELECT * FROM fuel_logs
      WHERE vehicle_id = $1
        AND (date < $2 OR (date = $2 AND odometer < $3))
      ORDER BY date DESC, odometer DESC
      LIMIT 1
    `;
    
    const result = await this.pool.query(query, [vehicleId, date, odometer]);
    
    if (result.rows.length === 0) {
      return null;
    }
    
    return this.mapRow(result.rows[0]);
  }

  async update(id: string, data: Partial<FuelLog>): Promise<FuelLog | null> {
    const fields = [];
    const values = [];
    let paramCount = 1;

    // Build dynamic update query
    if (data.date !== undefined) {
      fields.push(`date = $${paramCount++}`);
      values.push(data.date);
    }
    if (data.odometer !== undefined) {
      fields.push(`odometer = $${paramCount++}`);
      values.push(data.odometer);
    }
    if (data.gallons !== undefined) {
      fields.push(`gallons = $${paramCount++}`);
      values.push(data.gallons);
    }
    if (data.pricePerGallon !== undefined) {
      fields.push(`price_per_gallon = $${paramCount++}`);
      values.push(data.pricePerGallon);
    }
    if (data.totalCost !== undefined) {
      fields.push(`total_cost = $${paramCount++}`);
      values.push(data.totalCost);
    }
    if (data.station !== undefined) {
      fields.push(`station = $${paramCount++}`);
      values.push(data.station);
    }
    if (data.location !== undefined) {
      fields.push(`location = $${paramCount++}`);
      values.push(data.location);
    }
    if (data.notes !== undefined) {
      fields.push(`notes = $${paramCount++}`);
      values.push(data.notes);
    }
    // mpg column removed; efficiency is computed dynamically

    if (fields.length === 0) {
      return this.findById(id);
    }

    values.push(id);
    const query = `
      UPDATE fuel_logs
      SET ${fields.join(', ')}
      WHERE id = $${paramCount}
      RETURNING *
    `;

    const result = await this.pool.query(query, values);
    
    if (result.rows.length === 0) {
      return null;
    }
    
    return this.mapRow(result.rows[0]);
  }

  async batchInsert(
    logs: Array<CreateFuelLogRequest & { userId: string }>,
    client?: any
  ): Promise<FuelLog[]> {
    if (logs.length === 0) {
      return [];
    }

    // Multi-value INSERT for performance (avoids N round-trips)
    const queryClient = client || this.pool;
    const placeholders: string[] = [];
    const values: any[] = [];
    let paramCount = 1;

    logs.forEach((log) => {
      const logParams = [
        log.userId,
        log.vehicleId,
        log.date,
        log.odometer,
        log.gallons,
        log.pricePerGallon,
        log.totalCost,
        log.station,
        log.location,
        log.notes
      ];

      const placeholder = `($${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++})`;
      placeholders.push(placeholder);
      values.push(...logParams);
    });

    const query = `
      INSERT INTO fuel_logs (
        user_id, vehicle_id, date, odometer, gallons,
        price_per_gallon, total_cost, station, location, notes
      )
      VALUES ${placeholders.join(', ')}
      RETURNING *
    `;

    const result = await queryClient.query(query, values);
    return result.rows.map((row: any) => this.mapRow(row));
  }

  async delete(id: string): Promise<boolean> {
    const query = 'DELETE FROM fuel_logs WHERE id = $1';
    const result = await this.pool.query(query, [id]);
    return (result.rowCount ?? 0) > 0;
  }

  async getStats(vehicleId: string): Promise<FuelStats | null> {
    const query = `
      SELECT 
        COUNT(*) as log_count,
        SUM(gallons) as total_gallons,
        SUM(total_cost) as total_cost,
        AVG(price_per_gallon) as avg_price_per_gallon,
        MAX(odometer) - MIN(odometer) as total_miles
      FROM fuel_logs
      WHERE vehicle_id = $1
    `;
    
    const result = await this.pool.query(query, [vehicleId]);
    
    if (result.rows.length === 0 || result.rows[0].log_count === '0') {
      return null;
    }
    
    const row = result.rows[0];
    return {
      logCount: parseInt(row.log_count),
      totalGallons: parseFloat(row.total_gallons) || 0,
      totalCost: parseFloat(row.total_cost) || 0,
      averagePricePerGallon: parseFloat(row.avg_price_per_gallon) || 0,
      averageMPG: 0,
      totalMiles: parseInt(row.total_miles) || 0,
    };
  }

  private mapRow(row: any): FuelLog {
    return {
      id: row.id,
      userId: row.user_id,
      vehicleId: row.vehicle_id,
      date: row.date,
      odometer: row.odometer,
      gallons: parseFloat(row.gallons),
      pricePerGallon: parseFloat(row.price_per_gallon),
      totalCost: parseFloat(row.total_cost),
      station: row.station,
      location: row.location,
      notes: row.notes,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }

  // Enhanced API support (new schema)
  async createEnhanced(data: {
    userId: string;
    vehicleId: string;
    dateTime: Date;
    odometerReading?: number;
    tripDistance?: number;
    fuelType: string;
    fuelGrade?: string | null;
    fuelUnits: number;
    costPerUnit: number;
    totalCost: number;
    locationData?: any;
    notes?: string;
  }): Promise<any> {
    const query = `
      INSERT INTO fuel_logs (
        user_id, vehicle_id, date, date_time, odometer, trip_distance,
        fuel_type, fuel_grade, fuel_units, cost_per_unit,
        gallons, price_per_gallon, total_cost, location_data, notes
      )
      VALUES (
        $1, $2, $3, $4, $5, $6,
        $7, $8, $9, $10,
        $11, $12, $13, $14, $15
      )
      RETURNING *
    `;
    const values = [
      data.userId,
      data.vehicleId,
      data.dateTime.toISOString().slice(0, 10),
      data.dateTime,
      data.odometerReading ?? null,
      data.tripDistance ?? null,
      data.fuelType,
      data.fuelGrade ?? null,
      data.fuelUnits,
      data.costPerUnit,
      data.fuelUnits, // legacy support
      data.costPerUnit, // legacy support
      data.totalCost,
      data.locationData ?? null,
      data.notes ?? null
    ];
    const res = await this.pool.query(query, values);
    return res.rows[0] ? this.mapRow(res.rows[0]) : null;
  }

  async findByVehicleIdEnhanced(vehicleId: string): Promise<any[]> {
    const res = await this.pool.query(
      `SELECT * FROM fuel_logs WHERE vehicle_id = $1 ORDER BY date_time DESC NULLS LAST, date DESC NULLS LAST, created_at DESC`,
      [vehicleId]
    );
    return res.rows.map(row => this.mapRow(row));
  }

  async findByUserIdEnhanced(userId: string): Promise<any[]> {
    const res = await this.pool.query(
      `SELECT * FROM fuel_logs WHERE user_id = $1 ORDER BY date_time DESC NULLS LAST, date DESC NULLS LAST, created_at DESC`,
      [userId]
    );
    return res.rows.map(row => this.mapRow(row));
  }

  async findByIdEnhanced(id: string): Promise<any | null> {
    const res = await this.pool.query(`SELECT * FROM fuel_logs WHERE id = $1`, [id]);
    return res.rows[0] ? this.mapRow(res.rows[0]) : null;
  }

  async getPreviousLogByOdometer(vehicleId: string, odometerReading: number): Promise<any | null> {
    const res = await this.pool.query(
      `SELECT * FROM fuel_logs WHERE vehicle_id = $1 AND odometer IS NOT NULL AND odometer < $2 ORDER BY odometer DESC LIMIT 1`,
      [vehicleId, odometerReading]
    );
    return res.rows[0] ? this.mapRow(res.rows[0]) : null;
  }

  async getLatestLogForVehicle(vehicleId: string): Promise<any | null> {
    const res = await this.pool.query(
      `SELECT * FROM fuel_logs WHERE vehicle_id = $1 ORDER BY date_time DESC NULLS LAST, date DESC NULLS LAST, created_at DESC LIMIT 1`,
      [vehicleId]
    );
    return res.rows[0] ? this.mapRow(res.rows[0]) : null;
  }

  async updateEnhanced(id: string, data: {
    dateTime?: Date;
    odometerReading?: number;
    tripDistance?: number;
    fuelType?: string;
    fuelGrade?: string | null;
    fuelUnits?: number;
    costPerUnit?: number;
    locationData?: any;
    notes?: string;
  }): Promise<any | null> {
    const fields = [];
    const values = [];
    let paramCount = 1;

    // Build dynamic update query for enhanced schema
    if (data.dateTime !== undefined) {
      fields.push(`date_time = $${paramCount++}`);
      fields.push(`date = $${paramCount++}`);
      values.push(data.dateTime);
      values.push(data.dateTime.toISOString().slice(0, 10));
    }
    if (data.odometerReading !== undefined) {
      fields.push(`odometer = $${paramCount++}`);
      values.push(data.odometerReading);
    }
    if (data.tripDistance !== undefined) {
      fields.push(`trip_distance = $${paramCount++}`);
      values.push(data.tripDistance);
    }
    if (data.fuelType !== undefined) {
      fields.push(`fuel_type = $${paramCount++}`);
      values.push(data.fuelType);
    }
    if (data.fuelGrade !== undefined) {
      fields.push(`fuel_grade = $${paramCount++}`);
      values.push(data.fuelGrade);
    }
    if (data.fuelUnits !== undefined) {
      fields.push(`fuel_units = $${paramCount++}`);
      fields.push(`gallons = $${paramCount++}`); // legacy support
      values.push(data.fuelUnits);
      values.push(data.fuelUnits);
    }
    if (data.costPerUnit !== undefined) {
      fields.push(`cost_per_unit = $${paramCount++}`);
      fields.push(`price_per_gallon = $${paramCount++}`); // legacy support
      values.push(data.costPerUnit);
      values.push(data.costPerUnit);
    }
    if (data.locationData !== undefined) {
      fields.push(`location_data = $${paramCount++}`);
      values.push(data.locationData);
    }
    if (data.notes !== undefined) {
      fields.push(`notes = $${paramCount++}`);
      values.push(data.notes);
    }

    // Recalculate total cost if both fuelUnits and costPerUnit are present
    if (data.fuelUnits !== undefined && data.costPerUnit !== undefined) {
      fields.push(`total_cost = $${paramCount++}`);
      values.push(data.fuelUnits * data.costPerUnit);
    }

    if (fields.length === 0) {
      return this.findByIdEnhanced(id);
    }

    values.push(id);
    const query = `
      UPDATE fuel_logs
      SET ${fields.join(', ')}, updated_at = NOW()
      WHERE id = $${paramCount}
      RETURNING *
    `;

    const result = await this.pool.query(query, values);

    if (result.rows.length === 0) {
      return null;
    }

    return this.mapRow(result.rows[0]);
  }
}
