import { v4 as uuidv4 } from 'uuid';
import { db } from '../application/database';
import { ResponseError } from '../error/response-error';
import { PeriodResponse, toPeriodResponse, CreatePeriodRequest, Period } from '../model/period-model';
import { PeriodValidation } from '../validation/period-validation';
import { Validation } from '../validation/validation';

export class PeriodService {
  static async create(request: CreatePeriodRequest): Promise<PeriodResponse> {
    // Validate the create request
    const createRequest = Validation.validate(PeriodValidation.CREATE, request);

    // Generate a UUID for the new period
    const periodId = uuidv4();

    // Insert the new period into the database with the UUID
    await db.query('INSERT INTO period (id, category, start_date, end_date) VALUES (?, ?, ?, ?)', [
      periodId,
      createRequest.category,
      createRequest.start_date,
      createRequest.end_date,
    ]);

    // Retrieve the newly created period by the UUID
    const period = await db.queryOne<Period>('SELECT * FROM period WHERE id = ? LIMIT 1', [periodId]);

    if (!period) {
      throw new ResponseError(500, 'Failed to create period');
    }

    // Get all hotels
    const hotels = await db.query('SELECT * FROM hotel');

    // Create period prices for each hotel
    const periodPricesData = hotels.map((hotel) => ({
      hotel_id: hotel.id,
      period_id: period.id,
      price_double: 0,
      price_triple: 0,
      price_quad: 0,
    }));

    // Insert period prices into the database
    await db.query('INSERT INTO hotel_period_price (id, hotel_id, period_id, price_double, price_triple, price_quad) VALUES ?', [
      periodPricesData.map((price) => [uuidv4(), price.hotel_id, price.period_id, price.price_double, price.price_triple, price.price_quad]),
    ]);

    return toPeriodResponse(period);
  }

  static async update(id: string, request: CreatePeriodRequest): Promise<PeriodResponse> {
    // Validate the update request
    const updateRequest = Validation.validate(PeriodValidation.UPDATE, request);

    // Check if the period exists
    await this.#checkPeriodExist(id);

    // Update the period in the database
    await db.query('UPDATE period SET category = ?, start_date = ?, end_date = ? WHERE id = ?', [
      updateRequest.category,
      updateRequest.start_date,
      updateRequest.end_date,
      id,
    ]);

    // Retrieve the updated period by the UUID
    const period = await db.queryOne<Period>('SELECT * FROM period WHERE id = ? LIMIT 1', [id]);

    if (!period) {
      throw new ResponseError(500, 'Failed to update period');
    }

    return toPeriodResponse(period);
  }

  static async delete(id: string): Promise<PeriodResponse> {
    // Retrieve the period before deleting
    const period = await db.queryOne<Period>('SELECT * FROM period WHERE id = ? LIMIT 1', [id]);

    if (!period) {
      throw new ResponseError(500, 'Failed to delete period');
    }

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

    return toPeriodResponse(period);
  }

  static async getById(id: string): Promise<PeriodResponse> {
    // Retrieve a single period by ID
    const period = await db.queryOne<Period>('SELECT * FROM period WHERE id = ? LIMIT 1', [id]);

    if (!period) {
      throw new ResponseError(404, 'Period not found');
    }

    return toPeriodResponse(period);
  }

  static async getAll(): Promise<PeriodResponse[]> {
    // Retrieve all periods from the database
    const periods = await db.query<Period>('SELECT * FROM period ORDER BY start_date ASC');
    
    // Map each period to the response format
    return periods.map(toPeriodResponse);
  }

  static async #checkPeriodExist(id: string): Promise<void> {
    // Check if the period exists
    const period = await db.queryOne<{ id: string }>('SELECT id FROM period WHERE id = ? LIMIT 1', [id]);

    if (!period) {
      throw new ResponseError(404, 'Period not found');
    }
  }
}
