import { randomUUID } from 'crypto';
import type { Pool } from 'pg';
import type {
  CreateOwnershipCostRequest,
  UpdateOwnershipCostRequest,
  OwnershipCost,
  OwnershipCostResponse,
  OwnershipCostType,
  OwnershipCostStats
} from './ownership-costs.types';
import { OwnershipCostsRepository } from '../data/ownership-costs.repository';
import pool from '../../../core/config/database';

export class OwnershipCostsService {
  private readonly repo: OwnershipCostsRepository;
  private readonly db: Pool;

  constructor(dbPool?: Pool) {
    this.db = dbPool || pool;
    this.repo = new OwnershipCostsRepository(this.db);
  }

  async createCost(userId: string, body: CreateOwnershipCostRequest): Promise<OwnershipCost> {
    await this.assertVehicleOwnership(userId, body.vehicleId);

    const id = randomUUID();
    const cost = await this.repo.insert({
      id,
      userId,
      vehicleId: body.vehicleId,
      documentId: body.documentId,
      costType: body.costType,
      amount: body.amount,
      description: body.description,
      periodStart: body.periodStart,
      periodEnd: body.periodEnd,
      notes: body.notes,
    });

    return cost;
  }

  async getCost(userId: string, id: string): Promise<OwnershipCostResponse | null> {
    const cost = await this.repo.findById(id, userId);
    if (!cost) return null;
    return this.toResponse(cost);
  }

  async getCosts(userId: string, filters?: { vehicleId?: string; costType?: OwnershipCostType; documentId?: string }): Promise<OwnershipCostResponse[]> {
    const costs = await this.repo.findByUserId(userId, filters);
    return costs.map(c => this.toResponse(c));
  }

  async getCostsByVehicle(userId: string, vehicleId: string): Promise<OwnershipCostResponse[]> {
    const costs = await this.repo.findByVehicleId(vehicleId, userId);
    return costs.map(c => this.toResponse(c));
  }

  async getVehicleOwnershipCosts(vehicleId: string, userId: string): Promise<OwnershipCostStats> {
    const costs = await this.repo.findByVehicleId(vehicleId, userId);

    let totalCost = 0;
    let insuranceCosts = 0;
    let registrationCosts = 0;
    let taxCosts = 0;
    let otherCosts = 0;

    for (const c of costs) {
      if (c.amount === null || c.amount === undefined) continue;
      const amount = Number(c.amount);
      if (isNaN(amount)) {
        throw new Error(`Invalid amount value for ownership cost ${c.id}`);
      }

      totalCost += amount;

      // Breakdown by cost type for backward compatibility
      switch (c.costType) {
        case 'insurance':
          insuranceCosts += amount;
          break;
        case 'registration':
          registrationCosts += amount;
          break;
        case 'tax':
          taxCosts += amount;
          break;
        case 'inspection':
        case 'parking':
        case 'other':
          otherCosts += amount;
          break;
      }
    }

    return {
      totalCost,
      recordCount: costs.length,
      insuranceCosts,
      registrationCosts,
      taxCosts,
      otherCosts
    };
  }

  // Alias for backward compatibility with vehicles service
  async getVehicleCostStats(vehicleId: string, userId: string): Promise<OwnershipCostStats> {
    return this.getVehicleOwnershipCosts(vehicleId, userId);
  }

  async updateCost(userId: string, id: string, patch: UpdateOwnershipCostRequest): Promise<OwnershipCostResponse | null> {
    const existing = await this.repo.findById(id, userId);
    if (!existing) return null;

    // Convert nulls to undefined for repository compatibility
    const cleanPatch = Object.fromEntries(
      Object.entries(patch).map(([k, v]) => [k, v === null ? undefined : v])
    ) as Partial<Pick<OwnershipCost, 'documentId' | 'costType' | 'amount' | 'description' | 'periodStart' | 'periodEnd' | 'notes'>>;

    const updated = await this.repo.update(id, userId, cleanPatch);
    if (!updated) return null;
    return this.toResponse(updated);
  }

  async deleteCost(userId: string, id: string): Promise<void> {
    await this.repo.delete(id, userId);
  }

  private async assertVehicleOwnership(userId: string, vehicleId: string) {
    const res = await this.db.query('SELECT id FROM vehicles WHERE id = $1 AND user_id = $2', [vehicleId, userId]);
    if (!res.rows[0]) {
      const err: any = new Error('Vehicle not found or not owned by user');
      err.statusCode = 403;
      throw err;
    }
  }

  private toResponse(cost: OwnershipCost): OwnershipCostResponse {
    return cost;
  }
}
