"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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProductService = void 0;
const uuid_1 = require("uuid");
const database_1 = require("../application/database");
const response_error_1 = require("../error/response-error");
const product_validation_1 = require("../validation/product-validation");
const validation_1 = require("../validation/validation");
class ProductService {
    static create(request) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            // Validate the create request
            const createRequest = validation_1.Validation.validate(product_validation_1.ProductValidation.CREATE, request);
            // Generate a UUID for the new product
            const productId = (0, uuid_1.v4)();
            // Insert the new product into the database with the UUID
            yield database_1.db.query('INSERT INTO product (id, name, description, price, has_variation) VALUES (?, ?, ?, ?, ?)', [
                productId,
                createRequest.name,
                (_a = createRequest.description) !== null && _a !== void 0 ? _a : null,
                (_b = createRequest.price) !== null && _b !== void 0 ? _b : null,
                createRequest.has_variation,
            ]);
            // Insert the thumbnails into the database
            if (createRequest.thumbnails && createRequest.thumbnails.length > 0) {
                yield database_1.db.query('INSERT INTO product_thumbnail (id, product_id, image_url) VALUES ' +
                    createRequest.thumbnails.map(() => '(?, ?, ?)').join(', '), createRequest.thumbnails.flatMap((thumbnail) => [(0, uuid_1.v4)(), productId, thumbnail]));
            }
            // Insert the categories into the database
            if (createRequest.category_ids && createRequest.category_ids.length > 0) {
                yield database_1.db.query('INSERT INTO product_category (id, product_id, category_id) VALUES ' +
                    createRequest.category_ids.map(() => '(?, ?, ?)').join(', '), createRequest.category_ids.flatMap((category_id) => [(0, uuid_1.v4)(), productId, category_id]));
            }
            // Insert the variations into the database
            if (createRequest.variations && createRequest.variations.length > 0) {
                yield database_1.db.query('INSERT INTO product_variation (id, product_id, name, price) VALUES ' +
                    createRequest.variations.map(() => '(?, ?, ?, ?)').join(', '), createRequest.variations.flatMap((variation) => [(0, uuid_1.v4)(), productId, variation.name, variation.price]));
            }
            // Insert the includes into the database
            if (createRequest.includes && createRequest.includes.length > 0) {
                yield database_1.db.query('INSERT INTO product_include (id, product_id, description) VALUES ' +
                    createRequest.includes.map(() => '(?, ?, ?)').join(', '), createRequest.includes.flatMap((include) => [(0, uuid_1.v4)(), productId, include]));
            }
            // Insert the excludes into the database
            if (createRequest.excludes && createRequest.excludes.length > 0) {
                yield database_1.db.query('INSERT INTO product_exclude (id, product_id, description) VALUES ' +
                    createRequest.excludes.map(() => '(?, ?, ?)').join(', '), createRequest.excludes.flatMap((exclude) => [(0, uuid_1.v4)(), productId, exclude]));
            }
            return yield this.get(productId);
        });
    }
    static getAll(queryParams) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            // Validate the query parameters
            const queryRequest = validation_1.Validation.validate(product_validation_1.ProductValidation.QUERY, queryParams);
            // Calculate the skip value
            const skip = (queryRequest.page - 1) * queryRequest.limit;
            // Create the WHERE clause
            let filterQuery = 'WHERE 1 = 1';
            const filterParams = [];
            if (queryRequest.name) {
                filterQuery += ' AND name LIKE ?';
                filterParams.push(`%${queryRequest.name}%`);
            }
            if (queryRequest.has_variation !== undefined) {
                filterQuery += ' AND has_variation = ?';
                filterParams.push(queryRequest.has_variation);
            }
            if ((_a = queryRequest.category_id) === null || _a === void 0 ? void 0 : _a.length) {
                filterQuery += ' AND category_id IN (?)';
                filterParams.push(queryRequest.category_id);
            }
            // Create the ORDER BY clause
            const sortField = queryRequest.sort || 'created_at';
            const sortOrder = queryRequest.order || 'desc';
            // Query to get product details along with related entities
            const productsQuery = `
        SELECT product.*, 
          CONCAT(
            '[', GROUP_CONCAT(DISTINCT IF(thumbnail.id IS NOT NULL, JSON_OBJECT('id', thumbnail.id, 'increment', thumbnail.increment, 'image_url', thumbnail.image_url), NULL) ORDER BY thumbnail.increment), ']'
          ) AS thumbnails,
          CONCAT(
              '[', GROUP_CONCAT(DISTINCT IF(category.id IS NOT NULL, JSON_OBJECT('id', category.id, 'name', category.name), NULL)), ']'
          ) AS categories,
          CONCAT(
              '[', GROUP_CONCAT(DISTINCT IF(variation.id IS NOT NULL, JSON_OBJECT('id', variation.id, 'increment', variation.increment, 'name', variation.name, 'price', variation.price), NULL) ORDER BY variation.increment), ']'
          ) AS variations,
          CONCAT(
              '[', GROUP_CONCAT(DISTINCT IF(include.id IS NOT NULL, JSON_OBJECT('id', include.id, 'increment', include.increment, 'description', include.description), NULL) ORDER BY include.increment), ']'
          ) AS includes,
          CONCAT(
              '[', GROUP_CONCAT(DISTINCT IF(exclude.id IS NOT NULL, JSON_OBJECT('id', exclude.id, 'increment', exclude.increment, 'description', exclude.description), NULL) ORDER BY exclude.increment), ']'
          ) AS excludes
        FROM product
        LEFT JOIN product_thumbnail thumbnail ON product.id = thumbnail.product_id
        LEFT JOIN product_category pc ON product.id = pc.product_id
        LEFT JOIN category ON pc.category_id = category.id
        LEFT JOIN product_variation variation ON product.id = variation.product_id
        LEFT JOIN product_include include ON product.id = include.product_id
        LEFT JOIN product_exclude exclude ON product.id = exclude.product_id
        ${filterQuery}
        GROUP BY product.id
        ORDER BY ${sortField} ${sortOrder}
        LIMIT ? OFFSET ?
    `;
            const products = yield database_1.db.query(productsQuery, [...filterParams, queryRequest.limit, skip]);
            // Parse JSON-like fields in each product
            products.forEach((product) => {
                product.thumbnails = product.thumbnails && product.thumbnails !== 'null' ? JSON.parse(product.thumbnails) : [];
                product.categories = product.categories && product.categories !== 'null' ? JSON.parse(product.categories) : [];
                product.variations = product.variations && product.variations !== 'null' ? JSON.parse(product.variations) : [];
                product.includes = product.includes && product.includes !== 'null' ? JSON.parse(product.includes) : [];
                product.excludes = product.excludes && product.excludes !== 'null' ? JSON.parse(product.excludes) : [];
            });
            // Get the total count
            const count = yield database_1.db.queryOne('SELECT COUNT(DISTINCT product.id) AS total FROM product LEFT JOIN product_category ON product.id = product_category.product_id LEFT JOIN category ON product_category.category_id = category.id ' +
                filterQuery, filterParams);
            if (!count) {
                throw new response_error_1.ResponseError(404, 'Produk tidak ditemukan');
            }
            return {
                data: products,
                pagination: {
                    total: count.total,
                    current_page: queryRequest.page,
                    total_pages: Math.ceil(count.total / queryRequest.limit),
                    limit: queryRequest.limit,
                },
            };
        });
    }
    static get(id) {
        return __awaiter(this, void 0, void 0, function* () {
            // Query to get product details along with related entities
            const productQuery = `
        SELECT product.*, 
          CONCAT(
            '[', GROUP_CONCAT(DISTINCT IF(thumbnail.id IS NOT NULL, JSON_OBJECT('id', thumbnail.id, 'increment', thumbnail.increment, 'image_url', thumbnail.image_url), NULL) ORDER BY thumbnail.increment), ']'
          ) AS thumbnails,
          CONCAT(
              '[', GROUP_CONCAT(DISTINCT IF(category.id IS NOT NULL, JSON_OBJECT('id', category.id, 'name', category.name), NULL)), ']'
          ) AS categories,
          CONCAT(
              '[', GROUP_CONCAT(DISTINCT IF(variation.id IS NOT NULL, JSON_OBJECT('id', variation.id, 'increment', variation.increment, 'name', variation.name, 'price', variation.price), NULL) ORDER BY variation.increment), ']'
          ) AS variations,
          CONCAT(
              '[', GROUP_CONCAT(DISTINCT IF(include.id IS NOT NULL, JSON_OBJECT('id', include.id, 'increment', include.increment, 'description', include.description), NULL) ORDER BY include.increment), ']'
          ) AS includes,
          CONCAT(
              '[', GROUP_CONCAT(DISTINCT IF(exclude.id IS NOT NULL, JSON_OBJECT('id', exclude.id, 'increment', exclude.increment, 'description', exclude.description), NULL) ORDER BY exclude.increment), ']'
          ) AS excludes
        FROM product
        LEFT JOIN product_thumbnail thumbnail ON product.id = thumbnail.product_id
        LEFT JOIN product_category pc ON product.id = pc.product_id
        LEFT JOIN category ON pc.category_id = category.id
        LEFT JOIN product_variation variation ON product.id = variation.product_id
        LEFT JOIN product_include include ON product.id = include.product_id
        LEFT JOIN product_exclude exclude ON product.id = exclude.product_id
        WHERE product.id = ?
    `;
            const product = yield database_1.db.queryOne(productQuery, [id]);
            if (!product) {
                throw new response_error_1.ResponseError(404, 'Produk tidak ditemukan');
            }
            // Manually construct ProductResponse with parsed fields
            const formattedProduct = Object.assign(Object.assign({}, product), { description: product.description || '', thumbnails: product.thumbnails && product.thumbnails !== 'null' ? JSON.parse(product.thumbnails) : [], categories: product.categories && product.categories !== 'null' ? JSON.parse(product.categories) : [], variations: product.variations && product.variations !== 'null' ? JSON.parse(product.variations) : [], includes: product.includes && product.includes !== 'null' ? JSON.parse(product.includes) : [], excludes: product.excludes && product.excludes !== 'null' ? JSON.parse(product.excludes) : [] });
            return formattedProduct;
        });
    }
    static update(id, request) {
        return __awaiter(this, void 0, void 0, function* () {
            // Validate the update request parameters
            const updateRequest = validation_1.Validation.validate(product_validation_1.ProductValidation.UPDATE, request);
            return yield database_1.db.transaction((connection) => __awaiter(this, void 0, void 0, function* () {
                // Create an array to store the fields and values for the update query
                const fields = [];
                const values = [];
                // Add `name` to the update if provided
                if (updateRequest.name !== undefined) {
                    fields.push('name = ?');
                    values.push(updateRequest.name);
                }
                // Add `description` to the update if provided
                if (updateRequest.description !== undefined) {
                    fields.push('description = ?');
                    values.push(updateRequest.description);
                }
                // Add `price` to the update if provided
                if (updateRequest.price !== undefined) {
                    fields.push('price = ?');
                    values.push(updateRequest.price);
                }
                // Add `has_variation` to the update if provided
                if (updateRequest.has_variation !== undefined) {
                    fields.push('has_variation = ?');
                    values.push(updateRequest.has_variation);
                }
                // Update the product data in the database
                if (fields.length > 0) {
                    yield connection.query(`UPDATE product SET ${fields.join(', ')} WHERE id = ?`, [...values, id]);
                }
                // Update product thumbnails if provided
                if (updateRequest.thumbnails && updateRequest.thumbnails.length > 0) {
                    yield connection.query(`DELETE FROM product_thumbnail WHERE product_id = ?`, [id]);
                    const thumbnailValues = updateRequest.thumbnails.map((url) => [(0, uuid_1.v4)(), id, url]);
                    yield connection.query(`INSERT INTO product_thumbnail (id, product_id, image_url) VALUES ?`, [thumbnailValues]);
                }
                // Update product categories if provided
                if (updateRequest.category_ids) {
                    yield connection.query(`DELETE FROM product_category WHERE product_id = ?`, [id]);
                    if (updateRequest.category_ids.length > 0) {
                        const categoryValues = updateRequest.category_ids.map((category_id) => [(0, uuid_1.v4)(), id, category_id]);
                        yield connection.query(`INSERT INTO product_category (id, product_id, category_id) VALUES ?`, [categoryValues]);
                    }
                }
                // Update product variations if provided
                if (updateRequest.variations) {
                    yield connection.query(`DELETE FROM product_variation WHERE product_id = ?`, [id]);
                    if (updateRequest.variations.length > 0) {
                        const variationValues = updateRequest.variations.map((variation) => [
                            (0, uuid_1.v4)(),
                            id,
                            variation.name,
                            variation.price,
                        ]);
                        yield connection.query(`INSERT INTO product_variation (id, product_id, name, price) VALUES ?`, [
                            variationValues,
                        ]);
                    }
                }
                // Update product includes if provided
                if (updateRequest.includes) {
                    yield connection.query(`DELETE FROM product_include WHERE product_id = ?`, [id]);
                    if (updateRequest.includes.length > 0) {
                        const includeValues = updateRequest.includes.map((description) => [(0, uuid_1.v4)(), id, description]);
                        yield connection.query(`INSERT INTO product_include (id, product_id, description) VALUES ?`, [includeValues]);
                    }
                }
                // Update product excludes if provided
                if (updateRequest.excludes) {
                    yield connection.query(`DELETE FROM product_exclude WHERE product_id = ?`, [id]);
                    if (updateRequest.excludes.length > 0) {
                        const excludeValues = updateRequest.excludes.map((description) => [(0, uuid_1.v4)(), id, description]);
                        yield connection.query(`INSERT INTO product_exclude (id, product_id, description) VALUES ?`, [excludeValues]);
                    }
                }
                // Commit the transaction
                yield connection.commit();
                // Fetch and return the updated product details after transaction
                return yield this.get(id);
            }));
        });
    }
    static delete(id) {
        return __awaiter(this, void 0, void 0, function* () {
            // Check if the product exists
            const product = yield database_1.db.queryOne('SELECT * FROM product WHERE id = ?', [id]);
            if (!product) {
                throw new response_error_1.ResponseError(404, 'Produk tidak ditemukan');
            }
            // Delete the product
            yield database_1.db.query('DELETE FROM product WHERE id = ?', [id]);
        });
    }
}
exports.ProductService = ProductService;
