import { v4 as uuidv4 } from 'uuid';
import { db } from '../application/database';
import { ResponseError } from '../error/response-error';
import { CreateUserPackageRequest, UserPackageResponse } from '../model/user-package-model';
import { UserPackageValidation } from '../validation/user-package-validation';
import { Validation } from '../validation/validation';

export class UserPackageService {
  static async create(request: CreateUserPackageRequest): Promise<{ id: string }> {
    // Validate the create request
    const createRequest = Validation.validate(UserPackageValidation.CREATE, request);

    // Calculate the total price
    const total_price = await this.#calculateTotal(createRequest);

    // Generate a UUID for the new user package
    const userPackageId = uuidv4();

    // Create the user package
    await db.query(`
      INSERT INTO user_package (id, transportation_id, flight_id, hotel_mekkah_id, hotel_madinah_id, muthawif_id, handling_id, number_of_pax, travel_duration, mekkah_duration, madinah_duration, per_pax_price, total_price)
      VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
    `, [
      userPackageId,
      createRequest.transportation_id,
      createRequest.flight_id,
      createRequest.hotel_mekkah_id,
      createRequest.hotel_madinah_id,
      createRequest.muthawif_id,
      createRequest.handling_id,
      createRequest.number_of_pax,
      createRequest.travel_duration,
      createRequest.mekkah_duration,
      createRequest.madinah_duration,
      total_price / createRequest.number_of_pax,
      total_price,
    ]);

    return { id: userPackageId };
  }

  static async get(id: string): Promise<UserPackageResponse> {
    // Retrieve the user package by ID
    const userPackage = await db.queryOne<UserPackageResponse>(
      `
      SELECT
        up.*,
        t.name AS transportation,
        f.name AS flight,
        hm.name AS hotel_mekkah,
        hmd.name AS hotel_madinah,
        m.name AS muthawif,
        hd.name AS handling
      FROM user_package up
      JOIN user_package_option t ON up.transportation_id = t.id
      JOIN user_package_option f ON up.flight_id = f.id
      JOIN user_package_option hm ON up.hotel_mekkah_id = hm.id
      JOIN user_package_option hmd ON up.hotel_madinah_id = hmd.id
      JOIN user_package_option m ON up.muthawif_id = m.id
      JOIN user_package_option hd ON up.handling_id = hd.id
      WHERE up.id = ?
      `,
      [id],
    );

    if (!userPackage) {
      throw new ResponseError(404, 'User package not found');
    }

    return userPackage;
  }

  static async #calculateTotal(request: CreateUserPackageRequest): Promise<number> {
    // Retrieve the prices of each user package option
    const [transportation, flight, hotelMekkah, hotelMadinah, muthawif, handling] = await Promise.all([
      db.queryOne<{ price: string }>('SELECT price FROM user_package_option WHERE id = ? LIMIT 1', [request.transportation_id]),
      db.queryOne<{ price: string }>('SELECT price FROM user_package_option WHERE id = ? LIMIT 1', [request.flight_id]),
      db.queryOne<{ price: string }>('SELECT price FROM user_package_option WHERE id = ? LIMIT 1', [request.hotel_mekkah_id]),
      db.queryOne<{ price: string }>('SELECT price FROM user_package_option WHERE id = ? LIMIT 1', [request.hotel_madinah_id]),
      db.queryOne<{ price: string }>('SELECT price FROM user_package_option WHERE id = ? LIMIT 1', [request.muthawif_id]),
      db.queryOne<{ price: string }>('SELECT price FROM user_package_option WHERE id = ? LIMIT 1', [request.handling_id]),
    ]);
  
    // Check if any of the user package options are not found
    if (!transportation || !flight || !hotelMekkah || !hotelMadinah || !muthawif || !handling) {
      throw new ResponseError(404, 'User package items not found');
    }
  
    // Calculate the total price
    let total_price = 0;
    total_price += parseFloat(transportation.price);
    total_price += parseFloat(flight.price);
    total_price += parseFloat(hotelMekkah.price) * request.mekkah_duration;
    total_price += parseFloat(hotelMadinah.price) * request.madinah_duration;
    total_price += parseFloat(muthawif.price);
    total_price += parseFloat(handling.price);
    total_price *= request.number_of_pax;
  
    return total_price;
  }  
}
