"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _a, _HotelService_checkHotelExist, _HotelService_createHotel;
Object.defineProperty(exports, "__esModule", { value: true });
exports.HotelService = void 0;
const uuid_1 = require("uuid");
const database_1 = require("../application/database");
const response_error_1 = require("../error/response-error");
const hotel_validation_1 = require("../validation/hotel-validation");
const validation_1 = require("../validation/validation");
class HotelService {
    static create(request) {
        return __awaiter(this, void 0, void 0, function* () {
            // Validate the create request
            const createRequest = validation_1.Validation.validate(hotel_validation_1.HotelValidation.CREATE, request);
            // Create a new hotel in the database
            const hotelId = yield __classPrivateFieldGet(this, _a, "m", _HotelService_createHotel).call(this, createRequest);
            // Retrieve the created hotel by the ID
            return this.getById(hotelId);
        });
    }
    static update(id, request) {
        return __awaiter(this, void 0, void 0, function* () {
            // Validate the update request
            const updateRequest = validation_1.Validation.validate(hotel_validation_1.HotelValidation.UPDATE, request);
            // Check if the hotel exists
            yield __classPrivateFieldGet(this, _a, "m", _HotelService_checkHotelExist).call(this, id);
            // Update the hotel in the database
            yield database_1.db.query('UPDATE hotel SET name = ?, city = ?, order_number = ? WHERE id = ?', [
                updateRequest.name,
                updateRequest.city,
                updateRequest.order_number,
                id,
            ]);
            // Retrieve the updated hotel by the ID
            return this.getById(id);
        });
    }
    static bulkUpdate(request) {
        return __awaiter(this, void 0, void 0, function* () {
            // Validate the bulk update request
            const { modified, deleted } = validation_1.Validation.validate(hotel_validation_1.HotelValidation.BULK_UPDATE, request);
            // If modified is not empty, update or create the hotel
            if (modified && modified.length > 0) {
                yield Promise.all(modified.map((hotel) => __awaiter(this, void 0, void 0, function* () {
                    // Check if the hotel already exists
                    const existingHotel = yield database_1.db.queryOne('SELECT * FROM hotel WHERE id = ?', [hotel.hotel_id]);
                    // If the hotel does not exist, create a new hotel
                    if (!existingHotel) {
                        yield __classPrivateFieldGet(this, _a, "m", _HotelService_createHotel).call(this, {
                            vendor_id: hotel.vendor_id,
                            name: hotel.name,
                            city: hotel.city,
                            order_number: hotel.order_number,
                            prices: {
                                double: hotel.price_double,
                                triple: hotel.price_triple,
                                quad: hotel.price_quad,
                            },
                        });
                    }
                    else {
                        // Update the hotel
                        yield database_1.db.query('UPDATE hotel SET name = ?, order_number = ? WHERE id = ?', [
                            hotel.name,
                            hotel.order_number,
                            hotel.hotel_id,
                        ]);
                        // Update the hotel period prices
                        yield database_1.db.query('UPDATE hotel_period_price SET price_double = ?, price_triple = ?, price_quad = ? WHERE hotel_id = ? AND period_id = ?', [hotel.price_double, hotel.price_triple, hotel.price_quad, hotel.hotel_id, hotel.period_id]);
                    }
                })));
            }
            // If deleted is not empty, delete the hotels
            if (deleted && deleted.length > 0) {
                yield database_1.db.query('DELETE FROM hotel WHERE id IN (?)', [deleted]);
            }
        });
    }
    static delete(id) {
        return __awaiter(this, void 0, void 0, function* () {
            // Check if the hotel exists
            yield __classPrivateFieldGet(this, _a, "m", _HotelService_checkHotelExist).call(this, id);
            // Delete the hotel from the database
            yield database_1.db.query('DELETE FROM hotel WHERE id = ?', [id]);
        });
    }
    static getById(id) {
        return __awaiter(this, void 0, void 0, function* () {
            // Construct the query with specific field selection for a single hotel by ID
            const query = `
      SELECT 
        h.id AS hotel_id, h.name AS hotel_name, h.vendor_id, h.city, h.order_number,
        h.created_at AS hotel_created_at, h.updated_at AS hotel_updated_at,
        v.id AS vendor_id, v.name AS vendor_name, v.created_at AS vendor_created_at,
        v.updated_at AS vendor_updated_at,
        hp.id AS hotel_period_price_id, hp.hotel_id, hp.period_id, hp.price_double,
        hp.price_triple, hp.price_quad, hp.created_at AS hotel_period_price_created_at,
        hp.updated_at AS hotel_period_price_updated_at,
        p.id AS period_id, p.category, p.start_date, p.end_date,
        p.created_at AS period_created_at, p.updated_at AS period_updated_at
      FROM hotel h
      LEFT JOIN vendor v ON h.vendor_id = v.id
      LEFT JOIN hotel_period_price hp ON h.id = hp.hotel_id
      LEFT JOIN period p ON hp.period_id = p.id
      WHERE h.id = ?
      ORDER BY h.order_number ASC, h.created_at ASC
    `;
            // Execute the query with the hotel ID as the parameter
            const values = [id];
            const rows = yield database_1.db.query(query, values);
            if (rows.length === 0) {
                throw new response_error_1.ResponseError(404, 'Hotel not found');
            }
            // Aggregate data for a single hotel
            const hotel = rows.reduce((acc, row) => {
                if (!acc) {
                    // Create the hotel object if it doesn't exist in the accumulator
                    acc = {
                        id: row.hotel_id,
                        name: row.hotel_name,
                        city: row.city,
                        order_number: row.order_number,
                        vendor: {
                            id: row.vendor_id,
                            name: row.vendor_name,
                            created_at: row.vendor_created_at,
                            updated_at: row.vendor_updated_at,
                        },
                        periods: [],
                        created_at: row.hotel_created_at,
                        updated_at: row.hotel_updated_at,
                    };
                }
                // Append period and price details to the hotel periods array
                acc.periods.push({
                    price_double: Number(row.price_double),
                    price_triple: Number(row.price_triple),
                    price_quad: Number(row.price_quad),
                    period: {
                        id: row.period_id,
                        category: row.category,
                        start_date: row.start_date,
                        end_date: row.end_date,
                        created_at: row.period_created_at,
                        updated_at: row.period_updated_at,
                    },
                });
                return acc;
            }, null);
            if (!hotel) {
                throw new response_error_1.ResponseError(404, 'Hotel not found');
            }
            return hotel;
        });
    }
    static getAll(queryParams) {
        return __awaiter(this, void 0, void 0, function* () {
            // Validate the query parameters
            const queryRequest = validation_1.Validation.validate(hotel_validation_1.HotelValidation.QUERY, queryParams);
            // Construct the query with specific field selection
            const query = `
      SELECT 
        h.id AS hotel_id, h.name AS hotel_name, h.vendor_id, h.city, h.order_number,
        h.created_at AS hotel_created_at, h.updated_at AS hotel_updated_at,
        v.id AS vendor_id, v.name AS vendor_name, v.created_at AS vendor_created_at,
        v.updated_at AS vendor_updated_at,
        hp.id AS hotel_period_price_id, hp.hotel_id, hp.period_id, hp.price_double,
        hp.price_triple, hp.price_quad, hp.created_at AS hotel_period_price_created_at,
        hp.updated_at AS hotel_period_price_updated_at,
        p.id AS period_id, p.category, p.start_date, p.end_date,
        p.created_at AS period_created_at, p.updated_at AS period_updated_at
      FROM hotel h
      LEFT JOIN vendor v ON h.vendor_id = v.id
      LEFT JOIN hotel_period_price hp ON h.id = hp.hotel_id
      LEFT JOIN period p ON hp.period_id = p.id
      WHERE 1=1
      ${queryRequest.vendor_id ? 'AND h.vendor_id = ?' : ''}
      ${queryRequest.city ? 'AND h.city = ?' : ''}
      ${queryRequest.period_id ? 'AND hp.period_id = ?' : ''}
      ORDER BY h.order_number ASC, h.created_at ASC
    `;
            const values = [
                ...(queryRequest.vendor_id ? [queryRequest.vendor_id] : []),
                ...(queryRequest.city ? [queryRequest.city] : []),
                ...(queryRequest.period_id ? [queryRequest.period_id] : []),
            ];
            const rows = yield database_1.db.query(query, values);
            const hotels = rows.reduce((acc, row) => {
                let hotel = acc.find((h) => h.id === row.hotel_id);
                if (!hotel) {
                    hotel = {
                        id: row.hotel_id,
                        name: row.hotel_name,
                        city: row.city,
                        order_number: row.order_number,
                        vendor: {
                            id: row.vendor_id,
                            name: row.vendor_name,
                            created_at: row.vendor_created_at,
                            updated_at: row.vendor_updated_at,
                        },
                        periods: [],
                        created_at: row.hotel_created_at,
                        updated_at: row.hotel_updated_at,
                    };
                    acc.push(hotel);
                }
                hotel.periods.push({
                    price_double: Number(row.price_double),
                    price_triple: Number(row.price_triple),
                    price_quad: Number(row.price_quad),
                    period: {
                        id: row.period_id,
                        category: row.category,
                        start_date: row.start_date,
                        end_date: row.end_date,
                        created_at: row.period_created_at,
                        updated_at: row.period_updated_at,
                    },
                });
                return acc;
            }, []);
            return hotels;
        });
    }
    static getAllHotelPeriodPrices() {
        return __awaiter(this, void 0, void 0, function* () {
            // Construct the query to fetch hotels, vendors, and their period prices
            const query = `
      SELECT 
        h.id AS hotel_id, h.name AS hotel_name, h.vendor_id, h.city, h.order_number,
        h.created_at AS hotel_created_at, h.updated_at AS hotel_updated_at,
        v.id AS vendor_id, v.name AS vendor_name, v.created_at AS vendor_created_at,
        v.updated_at AS vendor_updated_at,
        hp.id AS hotel_period_price_id, hp.hotel_id, hp.period_id, hp.price_double,
        hp.price_triple, hp.price_quad, hp.created_at AS hotel_period_price_created_at,
        hp.updated_at AS hotel_period_price_updated_at,
        p.id AS period_id, p.category, p.start_date, p.end_date,
        p.created_at AS period_created_at, p.updated_at AS period_updated_at
      FROM hotel h
      LEFT JOIN vendor v ON h.vendor_id = v.id
      LEFT JOIN hotel_period_price hp ON h.id = hp.hotel_id
      LEFT JOIN period p ON hp.period_id = p.id
      ORDER BY h.order_number ASC
    `;
            // Execute the query to retrieve the data
            const rows = yield database_1.db.query(query);
            // Map rows to response format, grouping by hotel and handling nested relationships
            const hotels = rows.reduce((acc, row) => {
                let hotel = acc.find((h) => h.id === row.hotel_id);
                if (!hotel) {
                    // Create a new hotel entry if it doesn't already exist
                    hotel = {
                        id: row.hotel_id,
                        name: row.hotel_name,
                        city: row.city,
                        order_number: row.order_number,
                        vendor: {
                            id: row.vendor_id,
                            name: row.vendor_name,
                            created_at: row.vendor_created_at,
                            updated_at: row.vendor_updated_at,
                        },
                        periods: [],
                        created_at: row.hotel_created_at,
                        updated_at: row.hotel_updated_at,
                    };
                    acc.push(hotel);
                }
                // Add period pricing information to the hotel's periods array
                hotel.periods.push({
                    price_double: Number(row.price_double),
                    price_triple: Number(row.price_triple),
                    price_quad: Number(row.price_quad),
                    period: {
                        id: row.period_id,
                        category: row.category,
                        start_date: row.start_date,
                        end_date: row.end_date,
                        created_at: row.period_created_at,
                        updated_at: row.period_updated_at,
                    },
                });
                return acc;
            }, []);
            return hotels;
        });
    }
}
exports.HotelService = HotelService;
_a = HotelService, _HotelService_checkHotelExist = function _HotelService_checkHotelExist(id) {
    return __awaiter(this, void 0, void 0, function* () {
        // Check if the hotel exists
        const hotel = yield database_1.db.queryOne('SELECT * FROM hotel WHERE id = ?', [id]);
        if (!hotel) {
            throw new response_error_1.ResponseError(404, 'Hotel not found');
        }
    });
}, _HotelService_createHotel = function _HotelService_createHotel(data) {
    return __awaiter(this, void 0, void 0, function* () {
        // Create a new hotel in the database
        const hotelId = (0, uuid_1.v4)();
        yield database_1.db.query('INSERT INTO hotel (id, vendor_id, name, city, order_number) VALUES (?, ?, ?, ?, ?)', [
            hotelId,
            data.vendor_id,
            data.name,
            data.city,
            data.order_number,
        ]);
        // Retrieve all periods from the database
        const periods = yield database_1.db.query('SELECT * FROM period');
        // Create the hotel period prices in the database
        const periodPricesData = periods.map((period) => [
            (0, uuid_1.v4)(),
            hotelId,
            period.id,
            data.prices.double,
            data.prices.triple,
            data.prices.quad,
        ]);
        yield database_1.db.query('INSERT INTO hotel_period_price (id, hotel_id, period_id, price_double, price_triple, price_quad) VALUES ?', [periodPricesData]);
        return hotelId;
    });
};
