import { v4 as uuidv4 } from 'uuid';
import { db } from '../application/database';
import { ResponseError } from '../error/response-error';
import {
  ExchangeRateResponse,
  toExchangeRateResponse,
  CreateExchangeRateRequest,
  BulkUpdateExchangeRateRequest,
  ExchangeRate,
} from '../model/exchange-rate-model';
import { ExchangeRateValidation } from '../validation/exchange-rate-validation';
import { Validation } from '../validation/validation';

export class ExchangeRateService {
  static async create(request: CreateExchangeRateRequest): Promise<ExchangeRateResponse> {
    // Validate the create request
    const createRequest = Validation.validate(ExchangeRateValidation.CREATE, request);
    
    // Generate a UUID for the new exchange rate
    const exchangeRateId = uuidv4();

    // Insert the new exchange rate into the database with the UUID
    await db.query('INSERT INTO exchange_rate (id, currency, rate) VALUES (?, ?, ?)', [
      exchangeRateId,
      createRequest.currency,
      createRequest.rate_idr,
    ]);

    // Retrieve the newly created exchange rate by the UUID
    const exchangeRate = await db.queryOne<ExchangeRate>('SELECT * FROM exchange_rate WHERE id = ? LIMIT 1', [exchangeRateId]);

    if (!exchangeRate) {
      throw new ResponseError(500, 'Failed to create exchange rate');
    }
    
    return toExchangeRateResponse(exchangeRate);
  }

  static async bulkUpdate(request: BulkUpdateExchangeRateRequest[]): Promise<void> {
    // Validate the bulk update request
    const bulkUpdateRequest = Validation.validate(ExchangeRateValidation.BULK_UPDATE, request);

    // Start a transaction for batch processing
    await db.transaction(async (conn) => {
      for (const updateRequest of bulkUpdateRequest) {
        const fieldsToUpdate = [];
        const values = [];

        // Add `currency` to the update if provided
        if (updateRequest.currency !== undefined) {
          fieldsToUpdate.push('currency = ?');
          values.push(updateRequest.currency);
        }

        // Add `rate` to the update if provided
        if (updateRequest.rate_idr !== undefined) {
          fieldsToUpdate.push('rate_idr = ?');
          values.push(updateRequest.rate_idr);
        }

        // Add the exchange rate ID to the values
        values.push(updateRequest.id);

        // Update the exchange rate in the database
        await conn.query(`UPDATE exchange_rate SET ${fieldsToUpdate.join(', ')} WHERE id = ?`, values);
      }
    });
  }

  static async delete(id: string): Promise<void> {
    // Check if the exchange rate exists
    await this.#checkExchangeRateExist(id);

    // Delete the exchange rate from the database
    await db.query('DELETE FROM exchange_rate WHERE id = ?', [id]);
  }

  static async getAll(): Promise<ExchangeRateResponse[]> {
    // Retrieve all exchange rates from the database
    const exchangeRates = await db.query<ExchangeRate>('SELECT * FROM exchange_rate ORDER BY created_at ASC');

    // Map each exchange rate to the response
    return exchangeRates.map(toExchangeRateResponse);
  }

  static async get(id: string): Promise<ExchangeRateResponse> {
    // Retrieve a single exchange rate by ID
    const exchangeRate = await db.queryOne<ExchangeRate>('SELECT * FROM exchange_rate WHERE id = ? LIMIT 1', [id]);

    if (!exchangeRate) {
      throw new ResponseError(404, 'Exchange rate not found');
    }

    return toExchangeRateResponse(exchangeRate);
  }

  static async #checkExchangeRateExist(id: string): Promise<void> {
    // Retrieve the exchange rate by ID
    const exchangeRate = await db.queryOne<ExchangeRate>('SELECT * FROM exchange_rate WHERE id = ? LIMIT 1', [id]);

    if (!exchangeRate) {
      throw new ResponseError(404, 'Exchange rate not found');
    }
  }
}