createProduct doesn't save the correct output in the cache!!!
export const createProduct = async (req: Request, res: Response) => {
    try {
        console.log("Received product data:", req.body); // Log incoming data
        await query('BEGIN');

        const productData: CreateProductInput = req.body;

        // Adjust price if necessary
        let adjustedPrice = productData.price;
        if (productData.price < 1000) {
            adjustedPrice = parseFloat((productData.price * 1000).toFixed(2));
        }

        console.log("Inserting product with adjusted price:", adjustedPrice);

        // Insert product
        const productResult = await query(
            `INSERT INTO products (name, description, brand_id, has_color, has_size, product_type, price, qty, sold, created_at, updated_at)
             VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
             RETURNING id, created_at, updated_at`,
            [
                productData.name,
                productData.description,
                productData.brand_id,
                Boolean(productData.has_color),
                Boolean(productData.has_size),
                productData.product_type,
                adjustedPrice,
                productData.qty
            ]
        );
        const productId = productResult.rows[0].id;
        console.log(`Inserted product ID: ${productId}`);

        // Insert category associations
        const categoryInsertPromises = productData.category_ids.map((categoryId: number) => {
            console.log(`Associating category ID ${categoryId} with product ID ${productId}`);
            return query(
                `INSERT INTO product_categories (product_id, category_id) 
                 VALUES ($1, $2)`,
                [productId, categoryId]
            );
        });
        await Promise.all(categoryInsertPromises);
        console.log('Inserted all category associations.');

        // Insert images
        const imagesInsertPromises = productData.images
            .filter((image: ImageInput) => image.url) // Ensure URL exists
            .map((image: ImageInput) => {
                console.log(`Inserting product image: ${image.url}`);
                return query(
                    `INSERT INTO images (product_id, url, color_id, size_id)
       VALUES ($1, $2, $3, $4)
       RETURNING id, color_id, size_id, url`,
                    [productId, image.url, image.color_id || null, image.size_id || null]
                );
            });
        const insertedImagesResults = await Promise.all(imagesInsertPromises);
        const insertedImages = insertedImagesResults.map((imgResult: any) => ({
            id: imgResult.rows[0].id,
            url: imgResult.rows[0].url,
            color_id: imgResult.rows[0].color_id,
            size_id: imgResult.rows[0].size_id
        }));
        console.log(`Inserted ${insertedImages.length} product images.`);

        // Handle colors and sizes based on product type
        const insertedColors: number[] = [];
        const insertedSizes: number[] = [];

        if (productData.has_color && productData.colors) {
            for (const color of productData.colors) {
                // Adjust color price if necessary
                let adjustedColorPrice = color.price;
                if (color.price < 1000) {
                    adjustedColorPrice = parseFloat((color.price * 1000).toFixed(2));
                }

                console.log(`Inserting color: ${color.name} with has_size: ${color.has_size}`);

                // Insert color with explicit boolean casting
                const colorResult = await query(
                    `INSERT INTO colors (product_id, name, price, qty, sold, has_size)
                     VALUES ($1, $2, $3, $4, 0, $5)
                     RETURNING id, has_size`,
                    [productId, color.name, adjustedColorPrice, color.qty, Boolean(color.has_size)]
                );
                const colorId = colorResult.rows[0].id;
                const colorHasSize = colorResult.rows[0].has_size;
                console.log(`Inserted color ID=${colorId}, has_size=${colorHasSize}`);

                insertedColors.push(colorId);

                // Insert color images
                const colorImagesInsertPromises = color.images.map((image: ImageInput) => {
                    console.log(`Inserting color image: ${image.url}`);
                    return query(
                        `INSERT INTO images (product_id, color_id, url, size_id)
                         VALUES ($1, $2, $3, $4)
                         RETURNING id, color_id, size_id, url`,
                        [productId, colorId, image.url, image.size_id || null]
                    );
                });
                const insertedColorImagesResults = await Promise.all(colorImagesInsertPromises);
                console.log(`Inserted ${insertedColorImagesResults.length} images for color ID=${colorId}`);

                if (color.has_size && color.sizes) {
                    for (const size of color.sizes) {
                        // Adjust size price if necessary
                        let adjustedSizePrice = size.price;
                        if (size.price < 1000) {
                            adjustedSizePrice = parseFloat((size.price * 1000).toFixed(2));
                        }

                        console.log(`Inserting size: ${size.name} with price: ${adjustedSizePrice}`);

                        // Insert size
                        const sizeResult = await query(
                            `INSERT INTO sizes (product_id, color_id, name, price, qty, sold)
                             VALUES ($1, $2, $3, $4, $5, 0)
                             RETURNING id`,
                            [productId, colorId, size.name, adjustedSizePrice, size.qty]
                        );
                        const sizeId = sizeResult.rows[0].id;
                        console.log(`Inserted size ID=${sizeId} for color ID=${colorId}`);
                        insertedSizes.push(sizeId);

                        // Insert size images
                        const sizeImagesInsertPromises = size.images.map((image: ImageInput) => {
                            console.log(`Inserting size image: ${image.url}`);
                            return query(
                                `INSERT INTO images (product_id, color_id, size_id, url)
                                 VALUES ($1, $2, $3, $4)
                                 RETURNING id, color_id, size_id, url`,
                                [productId, colorId, sizeId, image.url]
                            );
                        });
                        const insertedSizeImagesResults = await Promise.all(sizeImagesInsertPromises);
                        console.log(`Inserted ${insertedSizeImagesResults.length} images for size ID=${sizeId}`);
                    }
                }
            }
        } else if (productData.has_size && productData.sizes) {
            for (const size of productData.sizes) {
                // Adjust size price if necessary
                let adjustedSizePrice = size.price;
                if (size.price < 1000) {
                    adjustedSizePrice = parseFloat((size.price * 1000).toFixed(2));
                }

                console.log(`Inserting standalone size: ${size.name} with price: ${adjustedSizePrice}`);

                // Insert size
                const sizeResult = await query(
                    `INSERT INTO sizes (product_id, name, price, qty, sold)
                     VALUES ($1, $2, $3, $4, 0)
                     RETURNING id`,
                    [productId, size.name, adjustedSizePrice, size.qty]
                );
                const sizeId = sizeResult.rows[0].id;
                console.log(`Inserted standalone size ID=${sizeId}`);
                insertedSizes.push(sizeId);

                // Insert size images
                const sizeImagesInsertPromises = size.images.map((image: ImageInput) => {
                    console.log(`Inserting standalone size image: ${image.url}`);
                    return query(
                        `INSERT INTO images (product_id, size_id, url)
                         VALUES ($1, $2, $3)
                         RETURNING id, color_id, size_id, url`,
                        [productId, sizeId, image.url]
                    );
                });
                const insertedSizeImagesResults = await Promise.all(sizeImagesInsertPromises);
                console.log(`Inserted ${insertedSizeImagesResults.length} images for standalone size ID=${sizeId}`);
            }
        }

        // Construct the new product object as it would appear in the cache
        const newProduct: Product = {
            id: productId,
            name: productData.name,
            description: productData.description,
            brand_id: productData.brand_id,
            has_color: productData.has_color,
            has_size: productData.has_size,
            product_type: productData.product_type,
            price: adjustedPrice,
            qty: productData.qty,
            sold: 0,
            created_at: productResult.rows[0].created_at.toISOString(),
            updated_at: productResult.rows[0].updated_at.toISOString(),
            categories: productData.category_ids.map((cid: number) => ({
                id: cid,
                name: "", // Optional: Fetch and populate category names
                main_category_id: 0, // Optional: Set appropriately
                main_category_name: "" // Optional: Set appropriately
            })),
            colors: productData.has_color && productData.colors ? productData.colors.map((color, index) => ({
                id: insertedColors[index],
                name: color.name,
                price: color.price || 0,
                qty: color.qty,
                sold: 0,
                has_size: Boolean(color.has_size),
                sizes: Boolean(color.has_size) && color.sizes ? color.sizes.map((size, sIndex) => ({
                    id: insertedSizes[sIndex],
                    name: size.name,
                    price: size.price,
                    qty: size.qty,
                    sold: 0,
                    color_id: null, // Optional: Set if needed
                    images: size.images.map(img => img.url)
                })) : [],
                images: color.images.map(img => img.url)
            })) : [],
            sizes: productData.has_size && !productData.has_color && productData.sizes ? productData.sizes.map((size, index) => ({
                id: insertedSizes[index],
                name: size.name,
                price: size.price,
                qty: size.qty,
                sold: 0,
                color_id: null,
                images: size.images.map(img => img.url)
            })) : [],
            images: insertedImages
        };

        console.log("New product to cache:", newProduct); // Log new product

        // Update the cache
        addProductToCache(newProduct);

        await query('COMMIT');
        res.status(201).json({ message: 'Product added successfully!', productId });
    } catch (error) {
        await query('ROLLBACK');
        console.error('Error creating product:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
};
example json:
{"products":[{"id":6,"name":"‏Anua Heartleaf Pore Clay Pack","description":"يقوم هذا القناع الطيني الكريمي بأمتصاص الزهم الزائد و إزالة الرؤوس السوداء بشكل فعال داخل المسام بسبب احتوائه Houttuynia Cordata الذي يتحكم في الدهون\nيحتوي على الكاولين لامتصاص الدهون\nيحتوي على مادة البنتونيت لتضييق المسام\nيحتوي على كيرسيتينول و هو المكون الرئيسي لأوراق القلب يوفر تهدئة البشرة ويتحكم في الدهون الزائدة لإزالة مشاكل المسام\nيعمل ايضا على إذابة خلايا الجلد الميتة القديمة على الجلد بلطف دون أي تهيج إضافة إلى تنعيم البشرة","brand_id":117,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T00:51:27.907Z","updated_at":"2024-09-10T16:58:17.276Z","categories":[{"id":175,"name":"Masks","main_category_id":1,"main_category_name":"Face Care"}],"colors":[],"sizes":[{"id":1418,"name":"100 ML","price":22000,"qty":118,"sold":0,"color_id":null,"images":["1048.webp"]}],"images":[{"id":1588,"url":"1048.webp","color_id":null,"size_id":1418}]},{"id":7,"name":"‏Anua Heartleaf Pore Control CleanSsing Oil","description":"غسول منظف زيتي بالهارت ليف للمسامات من انوا\nمنظف زيتي بتركيبة لا تسبب انسداد المسامات ويحتوي على زيوت نباتية (بما في ذلك زيت الجوجوبا وفاكهة الزيتون وبذور العنب)، ويخلص البشرة من الشوائب بشكل فعال مع ترك لمسة نهائية منتعشة ورطبة. مناسب للبشرة المعرضة للحبوب.\nمميزاته وفوائده:\n‏* non-comedogenic تركيبة لا تسد المسامات مناسبة للبشرة الدهنية والمعرضة للحبوب\n* مستخلص هارتليف (houttuynia cordata) معروف جيدًا بتأثيره المضاد للالتهابات والمهدئ للبشرة.\n* مزيل مكياج خفيف يساعد على إزالة الشوائب في المسامات.\n* لا يحتوي على أي مواد مهيجة.\n* يحافظ على رطوبة بشرتك بعد التنظيف.\n* يناسب جميع انواع البشرات حتى الحساسة.","brand_id":117,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T00:54:49.385Z","updated_at":"2024-09-28T13:21:34.815Z","categories":[{"id":168,"name":"Face Cleansers","main_category_id":1,"main_category_name":"Face Care"}],"colors":[],"sizes":[{"id":5043,"name":"200 ML","price":25000,"qty":100,"sold":0,"color_id":null,"images":[]}],"images":[{"id":9273,"url":"1049.webp","color_id":null,"size_id":null}]},{"id":9,"name":"‏La Roche Effaclar Astringent Lotion","description":"تونر للبشرة الدهنية و المعرضة للحبوب من la roche \n-يعمل على تقليل حجم المسام بشكل ملحوظ بعد إستخدامه بفضل مكوناته الفعٌالة التي تعمل على تنظيم الإفراز الدهني للوجه و تقشيره بلطف ويقلل من انسداد المسام المسببة لحب الشباب.\n-تلطيف البشرة وموازنة درجة الحموضة بعد خطوة التنظيف مما يعمل معادلة للبشره ولا يسبب جفاف للبشره \n-تنظيف عميق للمسام وإزالة بقايا الجلد الميت من مناطق الجلد التي غالبًا ما تُترك عند غسل وجهك.\n-تساعد المياه المعدنية La Roche-Posay على تهدئة البشرة وحمايتها ومضادات الأكسدة.","brand_id":70,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T15:29:54.092Z","updated_at":"2024-09-18T09:42:49.283Z","categories":[{"id":169,"name":"Toners","main_category_id":1,"main_category_name":"Face Care"}],"colors":[],"sizes":[{"id":1431,"name":"200 ml","price":25000,"qty":19,"sold":0,"color_id":null,"images":["1052.jpeg"]}],"images":[{"id":3283,"url":"1052.jpeg","color_id":null,"size_id":1431}]},{"id":11,"name":"Aveeno Daily Moisturising Hand Cream","description":"مرطب يد يومي من أفينو\nيتكون من دقيق الشوفان، تعمل هذه التركيبة سريعة الامتصاص على ترطيب البشرة لمدة 24 ساعة، فهو يساعد على حماية وتغذية اليد العادية إلى الجافة. يمنحك بشرة صحية وأكثر مرونة من الاستخدام الأول، لا يحتوي عطور و مناسب لبشرة اليد الحساسة.","brand_id":86,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T15:43:56.173Z","updated_at":"2024-09-10T21:04:29.869Z","categories":[{"id":163,"name":"Hand moisturiser","main_category_id":6,"main_category_name":"Hand Care"}],"colors":[],"sizes":[{"id":1430,"name":"75 ml","price":12000,"qty":10,"sold":0,"color_id":null,"images":["1056.jpeg"]}],"images":[{"id":1600,"url":"1056.jpeg","color_id":null,"size_id":1430}]},{"id":12,"name":"La Roche Posay Thermal Spring Water","description":"‏سبري ملطف ومرطب للبشره الحساسة المتهيجة بسبب العوامل الخارجية (الشمس ووسائل العناية بالبشرة وغيرها...) ويساعد على منع تقدم عمر الجلد. مضاد طبيعي للأكسدة.يمنح المزيج الفريد من المعادن والعناصر النادرة مثل السيلينيوم، ومياه الينابيع الحرارية من لاروش بوزيه خصائص مثبتة علميًا في الترطيب والنعومة ومضاد للأكسدة. تركيبة دقيقة. مناسب للكبار والأطفال.","brand_id":70,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T15:50:28.619Z","updated_at":"2024-09-28T13:06:40.801Z","categories":[{"id":193,"name":"Mist","main_category_id":1,"main_category_name":"Face Care"}],"colors":[],"sizes":[{"id":5042,"name":"150 ml","price":15000,"qty":50,"sold":0,"color_id":null,"images":[]}],"images":[{"id":9272,"url":"1057.jpeg","color_id":null,"size_id":null}]},{"id":39,"name":"Vichy Normaderm ‏Phytosolution Intensive Purifying Gel","description":"‏جل منظف ​​للبشرة الدهنية والمعرضة لحب الشباب، مدعوم بالمعادن والبروبايوتيك. يزيل جزيئات الغبار والتلوث بشكل مثالي من الاستعمال الأول.يزيل لمعان البشرة فوراً ويؤخر إنتاج الزيوت خلال النهار. يقلل مظهر المسام ويقضي بشكلٍ فعال على الرؤوس السوداء.\nينقي ويزيل المكياج \nيخفض إنتاج الزهم الزائد وزيوت البشرة\nخالي من الكبريتات - خالي من الصابون - خالي من الكحول","brand_id":71,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T19:11:33.876Z","updated_at":"2024-09-17T21:46:38.398Z","categories":[{"id":168,"name":"Face Cleansers","main_category_id":1,"main_category_name":"Face Care"}],"colors":[],"sizes":[{"id":896,"name":"200 ml","price":25000,"qty":14,"sold":0,"color_id":null,"images":["1737.jpeg"]}],"images":[{"id":3244,"url":"1737.jpeg","color_id":null,"size_id":896}]},{"id":41,"name":"‏Vichy Liftactiv A Retinol Specialist Deep Wrinkles Serum","description":"‏سيروم الريتنول من vichy\nيوفر فعالية بفضل تركيبته القوية لتقديم فوائد\nيعمل على تجديد البشرة ، ويعزز إنتاج الكولاجين ، وله خصائص مضادة للأكسدة. \n فهو فعال للغاية في تقليل التجاعيد العميقة بشكل واضح وتحسين تماسك البشرة. \nإلى جانب ذلك بفضل مفعولها المقوي تصبح البشرة أكثر  تناسقًا وإشراقًا. \nبفضل تقنية Retinol Guard ، فإن هذا المكون قادر على اختراق أعماق الجلد والعمل على جميع أنواع التجاعيد ، حتى أعمق التجاعيد \nملمسة حليبي يمتص بسرعة  ويترك ملمس غير دهني\n-مناسبة لجميع  أنواع البشرة.","brand_id":71,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T19:21:15.242Z","updated_at":"2024-09-08T12:08:37.048Z","categories":[{"id":170,"name":"Serums","main_category_id":1,"main_category_name":"Face Care"}],"colors":[],"sizes":[{"id":897,"name":"30 ml","price":55000,"qty":3,"sold":0,"color_id":null,"images":["1738.jpeg"]}],"images":[{"id":1073,"url":"1738.jpeg","color_id":null,"size_id":897}]},{"id":42,"name":"Vichy Dercos Densi-solutions spray","description":"بخاخ ڤيشي ديركوس لتكثيف وزياده سمك الشعر\n-ڤيشي دينسي يحتوي علي ماده ستيموكسيدين التي تعمل على تنشيط للخلايا الجذعيه والبصيلات الخامله في فروه الراس. \nوبالتالي إعاده إنبات الشعر من جديد ويزيد من كثافته. \n-يحتوي أيضًا ماده الرامنوز التي تحفز نمو الشعر وتغذيه.\n-الريسفيراتول مضاد أكسده قوي ضد العوامل اللي تؤذي الفروه ويزيد من كفاءه ماده الستيموكسدين.\n-١٠ بخات صباحا و ١٠ بخات مساءا على الفروه وتوزع جيدا .","brand_id":71,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T19:26:52.874Z","updated_at":"2024-09-08T12:05:14.585Z","categories":[{"id":9,"name":"Hair Treatments","main_category_id":3,"main_category_name":"Hair Care"}],"colors":[],"sizes":[{"id":895,"name":"100 ml","price":55000,"qty":1,"sold":0,"color_id":null,"images":["1736.jpeg"]}],"images":[{"id":1071,"url":"1736.jpeg","color_id":null,"size_id":895}]},{"id":43,"name":"Vichy Nutri Protein Restorative Shampoo","description":"شامبو مطور خصيصًا لمعالجة الجفاف بالشعر و الفروة بمزيج من المكونات الغذائية الطبيعية تصلح الجفاف الحاصل بسبب الحرارة و الاشعه الفوق البنفسجية يزيد ترطيب الشعر بشكل فوري يقلل من النهايات المتقصفة ينعم ملمس الشعر و يخلي حريري و صحي ولامع \nخالي من البارابين و السيليكون مناسب للاستخدام اليومي او اي وقت لغسل الشعر","brand_id":71,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T19:28:42.186Z","updated_at":"2024-09-17T21:48:35.090Z","categories":[{"id":1,"name":"Shampoos","main_category_id":3,"main_category_name":"Hair Care"}],"colors":[],"sizes":[{"id":892,"name":"250 ml","price":25000,"qty":1,"sold":0,"color_id":null,"images":["1733.jpeg"]}],"images":[{"id":3245,"url":"1733.jpeg","color_id":null,"size_id":892}]},{"id":44,"name":"Vichy ‏Beauty Deo Anti-perspirant","description":"‏مزيل تعرق طبي يساعد بعلاج التعرق المفرط ويخفي رائحة العرق المزعجه \nبمفعول يصل لمده 48 ساعة \nبالاضافه على قدرته بتفتيح منطقة الابط وتبيضها\nملائم للجلد الحساس يحتوي على حمض الليبوهيدروكسي و الحبيبات اللؤلؤية تعمل على تحسين مظهر البشرة وتفتيحها","brand_id":71,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T19:37:55.009Z","updated_at":"2024-09-17T11:16:11.775Z","categories":[{"id":182,"name":"Deodorant","main_category_id":2,"main_category_name":"Body Care"}],"colors":[],"sizes":[{"id":886,"name":"50 ml","price":20000,"qty":14,"sold":0,"color_id":null,"images":["1728.jpeg"]}],"images":[{"id":2956,"url":"1728.jpeg","color_id":null,"size_id":886}]},{"id":45,"name":"VICHY DEODORANT MINERAL 48 H","description":"مزيل عرق من فيجي\nخالي من أملاح الألمنيوم، مع أقصى درجة من الفعالية والكفاءة، من خلال ربط المعادن الموجودة في مياه فيتشي الحرارية مع الzinc gluconate \nلفعالية تستمر لمدة 48 ساعة بالاضافة الى  Provitamine B5 الذي يعمل كمهدئ للبشرة.\nمناسب لجميع انواع البشرة خاصة البشرة الحساسة\nخالي من الكحول والبرابين.","brand_id":71,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T19:40:27.610Z","updated_at":"2024-09-17T11:17:48.913Z","categories":[{"id":182,"name":"Deodorant","main_category_id":2,"main_category_name":"Body Care"}],"colors":[],"sizes":[{"id":888,"name":"50 ml","price":20000,"qty":33,"sold":0,"color_id":null,"images":["1730.jpeg"]}],"images":[{"id":2961,"url":"1730.jpeg","color_id":null,"size_id":888}]},{"id":46,"name":"Vichy Clinical Control Deodorant & Antiperspirant 96 hour","description":"Vichy Clinical Control Deodorant & Antiperspirant 96 hour \nديودرنت مزيل للتعرق بفاعليته المقاومة للتعرق حتى تحت الضغوط اليومية.\nيناسب الرجال و النساء الذين يعانون من التعرق المفرط و يريدون الجمع بين مزيل للعرق فعال و كفاءة عالية تتحمله بشرتهم.\nخالي من الباربين والكحول يناسب البشره الحساسة","brand_id":71,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T19:42:13.987Z","updated_at":"2024-09-23T19:02:26.409Z","categories":[{"id":182,"name":"Deodorant","main_category_id":2,"main_category_name":"Body Care"}],"colors":[],"sizes":[{"id":887,"name":"50 ml","price":20000,"qty":1,"sold":0,"color_id":null,"images":["1729.webp"]}],"images":[{"id":26,"url":"1729.webp","color_id":null,"size_id":887}]},{"id":47,"name":"Vichy Deodorant Anti-Transpirant Stress resist","description":"تركيبة لطيفة ، تستخدم خصيصا للبشرة الحساسة او المتهيجة\n‏‎لا يوجد اي بقايا لزجة أو دهنية او علامات بيضاء على \nالبشرة\nيزيل العرق لمدة 48 الى 72 ساعة وفعالية مضاد للعرق\n‏‎لايسبب الحساسية\nخالي من الكحول و البارابين و العطر","brand_id":71,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T19:51:07.220Z","updated_at":"2024-09-23T19:03:13.596Z","categories":[{"id":182,"name":"Deodorant","main_category_id":2,"main_category_name":"Body Care"}],"colors":[],"sizes":[{"id":894,"name":"50 ml","price":20000,"qty":24,"sold":0,"color_id":null,"images":["1735.jpeg"]}],"images":[{"id":27,"url":"1735.jpeg","color_id":null,"size_id":894}]},{"id":48,"name":"‏Vichy Traitement Anti-Transpirant 48H Roll-On","description":"-مزيل العرق الطبي من فيشي\n-مناسب للرجال والنساء الذين يعانون من فرط التعرق\n-يمتاز تركيبته \" الهيبوالرجينيك \" بدقة بالغة تعمل على مقاومة التعرق بدون سد مسام العرق ، ما يعطي حماية أفضل على مدار 48 ساعة بدون أي أضرار .\n-مناسب للبشره الحساسةو لاتحتوي على الكحول ، ولاتحتوي على البارابين .\n-يمنح البشرة ملمساً ناعماً بدون الشعور بعدم الراحة ، أو ترك علامات بيضاء على الجلد أو الملابس .\n-يمنحك شعورًا بالانتعاش عند الاستعمال ، كما يترك طبقة بودرة رقيقة غير لزجة على البشرة .\n-لا يؤدي إلى إلحاق أي أذى بالبشرة حتى مع إزالة الشعر الزائد بالليزر أو بأي طريقة أخرى .\n-يمتاز بتركيبة ناعمة ، تجف بسرعة دون إزعاج البشرة","brand_id":71,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T19:52:17.485Z","updated_at":"2024-09-23T19:00:22.400Z","categories":[{"id":182,"name":"Deodorant","main_category_id":2,"main_category_name":"Body Care"}],"colors":[],"sizes":[{"id":889,"name":"50 ml","price":20000,"qty":12,"sold":0,"color_id":null,"images":["1731.jpeg"]}],"images":[{"id":25,"url":"1731.jpeg","color_id":null,"size_id":889}]},{"id":49,"name":"Vichy Dercos Dry Shampoo","description":"شامبو جاف للشعر من فيشي\nيوفر مفعولًا مضادًا للإفرازات الدهنية بفضل التركيبة الغنية بالفيتامينات PP و B5 والبيوتين\nو يسمح هذا المزيج من المكونات النشطة للشعر باستعادة الحجم والملمس\nمضاد للحساسية و مناسب لفروة الرأس الحساسة\nطريقة الاستخدام : قبل استخدام الشامبو الجاف , مشط الشعر جيدا لتفكيك الجذو ثم رش وبعدها قم بتمشيط الشعر","brand_id":71,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-08-24T19:55:01.394Z","updated_at":"2024-09-17T15:04:36.810Z","categories":[{"id":3,"name":"Dry Shampoos","main_category_id":3,"main_category_name":"Hair Care"}],"colors":[],"sizes":[{"id":893,"name":"150 ml","price":30000,"qty":0,"sold":0,"color_id":null,"images":["1734.jpeg"]}],"images":[{"id":3144,"url":"1734.jpeg","color_id":null,"size_id":893}]}],"total":826,"page":1,"itemsPerPage":15,"totalPages":56,"hasMore":true,"minPrice":0,"maxPrice":250000}
what it saves:
{"products":[{"id":861,"name":"Aanu Green Lemon Vitamin C Blemish Serum","description":"Anua سيروم فيتامين سي من\nيتكون هذا السيروم من مستخلص الليمون الأخضر و فيتامين C.\nيحتوي على كبسولات مملوءة بالبانثينول والجلوتاثيون والبيسابولول لتعزيز تأثيرات التفتيح والتهدئة مما يجعله مثاليًا لمحاربة فرط التصبغ. \nتهدف هذه التركيبة المدعومة علميًا إلى تقليل ظهور الشوائب على السطح وفي أعماق الجلد. \nكما أنه يساعد على تفتيح وتقليل التجاعيد بسبب مزيج فيتامين E وحمض الفيروليك وألفا بيسابولول والأدينوزين.","brand_id":117,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-09-12T12:02:59.661Z","updated_at":"2024-09-12T12:02:59.661Z","categories":[{"id":170,"name":"Serums","main_category_id":1,"main_category_name":"Face Care"}],"colors":[],"sizes":[{"id":1697,"name":"20 ml","price":35000,"qty":11,"sold":0,"color_id":null,"images":["1984.jpeg"]}],"images":[{"id":1842,"url":"1984.jpeg","color_id":null,"size_id":1697}]},{"id":5016,"name":"AA","description":"aa","brand_id":91,"has_color":false,"has_size":true,"product_type":"size","price":0,"qty":0,"sold":0,"created_at":"2024-09-28T20:55:21.424Z","updated_at":"2024-09-28T20:55:21.424Z","categories":[{"id":163,"name":"","main_category_id":0,"main_category_name":""}],"colors":[],"sizes":[{"id":5054,"name":"40","price":40,"qty":40,"sold":0,"color_id":null,"images":["5029.jpg"]}],"images":[]}],"total":2,"page":1,"itemsPerPage":15,"totalPages":1,"hasMore":false,"minPrice":0,"maxPrice":250000}

however createProduct works absolutely fine! it adds all the information correctly to the database and after revalidating the cache everything gets back to normal... however here there are so much issues like a lot of properties doesn't even exist in the original json and the price if less than 1000 it should be multiplied by 1000, the images are incorrect, and so much more wrong stuff!!!
full productController.ts:
import { Request, Response } from 'express';
import {query} from "../config/database";
import {brandsCache, productCache} from "../cache";
import {
    Brand,
    BrandsCacheData,
    CacheData, Color, CreateProductInput,
    Image,
    ImageInput,
    Product, Size,
    UpdateProductInput
} from "product";
import {addProductToCache, deleteProductFromCache, updateProductInCache} from "../cacheHelper";

const fetchCategoryDetails = async (subcategoryIds: number[]) => {
    const categoriesResult = await query(
        `SELECT sc.id AS subcategory_id, sc.name AS subcategory_name,
                c.id AS main_category_id, c.name AS main_category_name
         FROM subcategories sc
         JOIN categories c ON sc.category_id = c.id
         WHERE sc.id = ANY($1::int[])`,
        [subcategoryIds]
    );

    return categoriesResult.rows.map((row: any) => ({
        id: row.subcategory_id,
        name: row.subcategory_name,
        main_category_id: row.main_category_id,
        main_category_name: row.main_category_name
    }));
};
export const loadAndCacheAllProducts = async (): Promise<CacheData> => {
    const q = `
        SELECT 
            p.id,
            p.name,
            p.description,
            p.brand_id,
            p.has_color,
            p.has_size,
            p.product_type,
            p.price,
            p.qty,
            p.sold,
            p.created_at,
            p.updated_at,
            sc.id AS subcategory_id,
            sc.name AS subcategory_name,
            c.id AS main_category_id,
            c.name AS main_category_name,
            s.id AS size_id,
            s.name AS size_name,
            s.price AS size_price,
            s.qty AS size_qty,
            s.sold AS size_sold,
            cl.id AS color_id,
            cl.name AS color_name,
            cl.price AS color_price,
            cl.qty AS color_qty,
            cl.sold AS color_sold,
            i.id AS image_id,
            i.url AS image_url,
            i.color_id AS image_color_id,
            i.size_id AS image_size_id
        FROM products p
        LEFT JOIN product_categories pc ON p.id = pc.product_id
        LEFT JOIN subcategories sc ON pc.category_id = sc.id
        LEFT JOIN categories c ON sc.category_id = c.id
        LEFT JOIN colors cl ON p.id = cl.product_id
        LEFT JOIN sizes s ON p.id = s.product_id
        LEFT JOIN images i ON (p.id = i.product_id AND (cl.id = i.color_id OR i.color_id IS NULL) AND (s.id = i.size_id OR i.size_id IS NULL))
    `;

    try {
        const result = await query(q, []);
        const productsMap: Record<number, Product> = {};

        // Temporary map to hold sizes that need to be assigned to colors
        const sizesMap: Record<number, Size> = {};

        // First pass: Initialize products and collect sizes
        result.rows.forEach((row: any) => {
            const productId = Number(row.id);

            // Initialize the product in the map if it doesn't exist
            if (!productsMap[productId]) {
                productsMap[productId] = {
                    id: productId,
                    name: row.name,
                    description: row.description,
                    brand_id: Number(row.brand_id),
                    has_color: Boolean(row.has_color),
                    has_size: Boolean(row.has_size),
                    product_type: row.product_type,
                    price: Number(row.price),
                    qty: Number(row.qty),
                    sold: Number(row.sold),
                    created_at: new Date(row.created_at).toISOString(),
                    updated_at: new Date(row.updated_at).toISOString(),
                    categories: [],
                    colors: [],
                    sizes: [], // Will be populated only if has_color is false
                    images: []
                };
            }

            const product = productsMap[productId];

            // Add category if present and not already added
            if (
                row.subcategory_id &&
                row.subcategory_name &&
                row.main_category_id &&
                row.main_category_name
            ) {
                const categoryId = Number(row.subcategory_id);
                const mainCategoryId = Number(row.main_category_id);
                const existingCategory = product.categories.find(cat => cat.id === categoryId);
                if (!existingCategory) {
                    product.categories.push({
                        id: categoryId,
                        name: row.subcategory_name,
                        main_category_id: mainCategoryId,
                        main_category_name: row.main_category_name,
                    });
                }
            }

            // Add color if present and not already added
            if (row.color_id && row.color_name && row.has_size !== undefined) {
                const colorId = Number(row.color_id);
                let color = product.colors.find(color => color.id === colorId);

                if (!color) {
                    color = {
                        id: colorId,
                        name: row.color_name,
                        price: Number(row.color_price),
                        qty: Number(row.color_qty),
                        sold: Number(row.color_sold),
                        has_size: Boolean(row.has_size),
                        sizes: [], // Will be populated if has_size is true
                        images: []
                    };
                    product.colors.push(color);
                }
            }

            // Collect sizes to assign later based on color association
            if (row.size_id && row.size_name) {
                const sizeId = Number(row.size_id);
                const colorId = row.color_id ? Number(row.color_id) : null;

                // If the size is associated with a color, store it for later assignment
                if (colorId && product.has_color) {
                    sizesMap[sizeId] = {
                        id: sizeId,
                        name: row.size_name,
                        price: Number(row.size_price),
                        qty: Number(row.size_qty),
                        sold: Number(row.size_sold),
                        color_id: colorId,
                        images: []
                    };
                } else if (!product.has_color && product.has_size) {
                    // If the product has sizes but no colors, assign size directly
                    if (!product.sizes.find(s => s.id === sizeId)) {
                        product.sizes.push({
                            id: sizeId,
                            name: row.size_name,
                            price: Number(row.size_price),
                            qty: Number(row.size_qty),
                            sold: Number(row.size_sold),
                            color_id: null,
                            images: []
                        });
                    }
                }
            }

            // Collect images to assign
            if (row.image_id && row.image_url) {
                const imageId = Number(row.image_id);
                // Avoid duplicate images
                if (!product.images.find(img => img.id === imageId)) {
                    product.images.push({
                        id: imageId,
                        url: row.image_url,
                        color_id: row.image_color_id ? Number(row.image_color_id) : null,
                        size_id: row.image_size_id ? Number(row.image_size_id) : null
                    });
                }

                // Associate image with color if applicable
                if (row.image_color_id) {
                    const colorId = Number(row.image_color_id);
                    const color = product.colors.find(c => c.id === colorId);
                    if (color && !color.images.includes(row.image_url)) {
                        color.images.push(row.image_url);
                    }
                }

                // Associate image with size if applicable and size is within a color
                if (row.image_size_id) {
                    const sizeId = Number(row.image_size_id);
                    const size = sizesMap[sizeId];
                    if (size) {
                        if (!size.images.includes(row.image_url)) {
                            size.images.push(row.image_url);
                        }
                    } else {
                        // If the product does not have colors, associate with product's sizes
                        const standaloneSize = product.sizes.find(s => s.id === Number(row.image_size_id));
                        if (standaloneSize && !standaloneSize.images.includes(row.image_url)) {
                            standaloneSize.images.push(row.image_url);
                        }
                    }
                }
            }
        });

        // Second pass: Assign sizes to their respective colors
        Object.values(productsMap).forEach(product => {
            if (product.has_color) {
                // Initialize all product.sizes as empty
                product.sizes = [];

                // Iterate over collected sizes and assign to colors
                Object.values(sizesMap).forEach(size => {
                    if (size.color_id === null) return; // Skip sizes without color_id

                    const color = product.colors.find(c => c.id === size.color_id);
                    if (color) {
                        color.sizes.push({
                            id: size.id,
                            name: size.name,
                            price: size.price,
                            qty: size.qty,
                            sold: size.sold,
                            color_id: size.color_id,
                            images: size.images
                        });
                    }
                });

                // Update has_size for each color based on whether it has sizes
                product.colors.forEach(color => {
                    color.has_size = color.sizes.length > 0;
                });
            }
            // If has_color is false, sizes are already assigned directly to product.sizes
        });

        const allProducts = Object.values(productsMap);

        // Calculate min and max prices across all products
        let minPrice = Infinity;
        let maxPrice = -Infinity;
        allProducts.forEach(product => {
            // Product price
            if (product.price < minPrice) minPrice = product.price;
            if (product.price > maxPrice) maxPrice = product.price;

            // Prices from colors and their sizes
            product.colors.forEach(color => {
                if (color.price < minPrice) minPrice = color.price;
                if (color.price > maxPrice) maxPrice = color.price;

                color.sizes.forEach(size => {
                    if (size.price < minPrice) minPrice = size.price;
                    if (size.price > maxPrice) maxPrice = size.price;
                });
            });

            // Prices from standalone sizes
            product.sizes.forEach(size => {
                if (size.price < minPrice) minPrice = size.price;
                if (size.price > maxPrice) maxPrice = size.price;
            });
        });

        // Fallback in case no prices are present
        if (minPrice === Infinity) minPrice = 0;
        if (maxPrice === -Infinity) maxPrice = 0;

        // Cache the data with a consistent key
        const cacheData: CacheData = { allProducts, minPrice, maxPrice };
        productCache.set('allProducts', cacheData);
        console.log(`Cached ${allProducts.length} products with minPrice: ${minPrice} and maxPrice: ${maxPrice}.`);

        return cacheData;
    } catch (error) {
        console.error('Error loading products from DB:', error);
        throw error; // Propagate error to be handled by caller
    }
};


/**
 * Fetches all brands from the database and caches them.
 * @returns {Promise<BrandsCacheData>} A promise that resolves to the cached brand data.
 */
const loadAndCacheAllBrands = async (): Promise<BrandsCacheData> => {
    const q = `
        SELECT s.*, c.name as category_name 
        FROM subcategories s 
        JOIN categories c ON s.category_id = c.id AND c.is_brand
        ORDER BY c.name, s.name
    `;

    try {
        const result = await query(q, []);
        const allBrands: Brand[] = result.rows.map((row: any) => ({
            id: Number(row.id),
            name: row.name,
            category_id: Number(row.category_id),
            category_name: row.category_name,
        }));

        // Cache the brands
        const brandsCacheData: BrandsCacheData = { allBrands };
        brandsCache.set('allBrands', brandsCacheData);
        console.log(`Cached ${allBrands.length} brands successfully.`);

        return brandsCacheData;
    } catch (error) {
        console.error('Error loading brands from DB:', error);
        throw error; // Propagate error to be handled by caller
    }
};

/**
 * Controller to handle fetching and responding with filtered products.
 * Caches all products and brands on first request and performs in-memory filtering thereafter.
 * @param {Request} req - Express request object containing query parameters.
 * @param {Response} res - Express response object to send JSON data.
 */
export const getProducts = async (req: Request, res: Response): Promise<void> => {
    try {
        // Retrieve cached products
        let cacheData: CacheData | undefined = productCache.get('allProducts');

        if (!cacheData) {
            // Cache : Load from DB and cache
            cacheData = await loadAndCacheAllProducts();
            console.log('Loaded products from database and cached them.');
        } else {
            console.log('Loaded products from cache.');
        }

        const { allProducts, minPrice, maxPrice } = cacheData;

        // Log total products before any filtering
        console.log(`Total products before filtering: ${allProducts.length}`);

        // Extract and sanitize query parameters
        let {
            search = '',
            c,
            sc,
            b,
            minPriceA = '0',
            maxPriceA = '',
            availability = 'all',
            sortBy = '',
            page = '1',
            itemsPerPage = '15',
        } = req.query;

        // Parse numerical values
        const pageNum = parseInt(page as string, 10) || 1;
        const itemsPerPageNum = parseInt(itemsPerPage as string, 10) || 15;
        const minPriceANum = parseFloat(minPriceA as string) || 0;
        let maxPriceANum = maxPriceA ? parseFloat(maxPriceA as string) : Infinity;

        // If maxPriceA is less than minPriceA, reset to Infinity
        if (!isNaN(maxPriceANum) && maxPriceANum < minPriceANum) {
            console.warn('Provided maxPriceA is less than minPriceA. Resetting maxPriceANum to Infinity.');
            maxPriceANum = Infinity;
        } else if (isNaN(maxPriceANum)) {
            maxPriceANum = Infinity;
        }

        // Parse c, sc, b as numbers if present
        const cNum = c ? parseInt(c as string, 10) : undefined;
        const scNum = sc ? parseInt(sc as string, 10) : undefined;
        const bNum = b ? parseInt(b as string, 10) : undefined;

        // Initialize brand-related variables if 'b' is provided
        let allBrands: Brand[] = [];
        if (bNum !== undefined && !isNaN(bNum)) {
            // Retrieve cached brands
            let brandsCacheData: BrandsCacheData | undefined = brandsCache.get('allBrands');

            if (!brandsCacheData) {
                // Cache miss: Load from DB and cache
                brandsCacheData = await loadAndCacheAllBrands();
                console.log('Loaded brands from database and cached them.');
            } else {
                console.log('Loaded brands from cache.');
            }
            console.log(brandsCacheData)
            allBrands = brandsCacheData.allBrands;

            // Check if 'bNum' is a valid brand ID
            const isValidBrand = allBrands.some(brand => brand.category_id === bNum);
            if (!isValidBrand) {
                console.warn(`Brand ID ${bNum} is invalid.`);
                // Return zero products since 'b' is invalid
                res.json({
                    products: [],
                    total: 0,
                    page: pageNum,
                    itemsPerPage: itemsPerPageNum,
                    totalPages: 0,
                    hasMore: false,
                    minPrice,
                    maxPrice,
                });
                return;
            }
        }

        // Start with all products
        let filteredProducts = [...allProducts];

        // Apply Search Filter
        if (search && typeof search === 'string') {
            const searchLower = search.toLowerCase();
            const searchWords = searchLower.split(/\s+/).filter(Boolean);
            filteredProducts = filteredProducts.filter(product =>
                searchWords.every(word => product.name.toLowerCase().includes(word))
            );
        }

        // Apply Category Filter (c)
        if (cNum !== undefined && !isNaN(cNum)) {
            filteredProducts = filteredProducts.filter(product =>
                product.categories.some(cat => cat.main_category_id === cNum)
            );
        }

        // Apply Subcategory Filter (sc)
        if (scNum !== undefined && !isNaN(scNum)) {
            filteredProducts = filteredProducts.filter(product =>
                product.brand_id === scNum ||
                product.categories.some(cat => cat.id === scNum)
            );
        }

        // Apply Brand Filter (b)
        if (bNum !== undefined && !isNaN(bNum)) {
            console.log(`Applying Brand Filter: brand_id === ${bNum}`);
            const preFilterCount = filteredProducts.length;
            allBrands = allBrands.filter(brand => brand.category_id === bNum)
            filteredProducts = filteredProducts.filter(product =>
                allBrands.some(brand => brand.id === product.brand_id)
            );
            console.log(`Filtered from ${preFilterCount} to ${filteredProducts.length} products after applying brand filter.`);
        }

        // Apply Price Range Filter
        if (minPriceANum > 0 || maxPriceANum < Infinity) {
            filteredProducts = filteredProducts.filter(product => {
                const productPrices = product.sizes.map(size => size.price);
                if (productPrices.length === 0) return false;
                const productMinPrice = Math.min(...productPrices);
                const productMaxPrice = Math.max(...productPrices);
                return productMinPrice >= minPriceANum && productMaxPrice <= maxPriceANum;
            });
        }

        // Apply Availability Filter
        if (availability === 'in_stock') {
            filteredProducts = filteredProducts.filter(product =>
                product.sizes.some(size => size.qty > 0)
            );
        } else if (availability === 'out_of_stock') {
            filteredProducts = filteredProducts.filter(product =>
                product.sizes.every(size => size.qty === 0)
            );
        }

        // Log total after filtering
        console.log(`Total products after filtering: ${filteredProducts.length}`);

        // Apply Sorting
        if (sortBy && typeof sortBy === 'string') {
            switch (sortBy) {
                case 'Price: Low to High':
                    filteredProducts.sort((a, b) => {
                        const aMin = Math.min(...a.sizes.map(s => s.price));
                        const bMin = Math.min(...b.sizes.map(s => s.price));
                        return aMin - bMin;
                    });
                    break;
                case 'Price: High to Low':
                    filteredProducts.sort((a, b) => {
                        const aMax = Math.max(...a.sizes.map(s => s.price));
                        const bMax = Math.max(...b.sizes.map(s => s.price));
                        return bMax - aMax;
                    });
                    break;
                case 'Name: A to Z':
                    filteredProducts.sort((a, b) => a.name.localeCompare(b.name));
                    break;
                case 'Name: Z to A':
                    filteredProducts.sort((a, b) => b.name.localeCompare(a.name));
                    break;
                case 'Best Selling':
                    filteredProducts.sort((a, b) => b.sold - a.sold);
                    break;
                case 'Newest Arrivals':
                    filteredProducts.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
                    break;
                default:
                    // If sortBy parameter doesn't match any case, do not sort
                    break;
            }
        }

        // Apply Pagination
        const totalFilteredProducts = filteredProducts.length;
        const totalPages = Math.ceil(totalFilteredProducts / itemsPerPageNum);
        const startIdx = (pageNum - 1) * itemsPerPageNum;
        const endIdx = startIdx + itemsPerPageNum;
        const paginatedProducts = filteredProducts.slice(startIdx, endIdx);

        // Construct Response
        const response = {
            products: paginatedProducts,
            total: totalFilteredProducts,
            page: pageNum,
            itemsPerPage: itemsPerPageNum,
            totalPages,
            hasMore: endIdx < totalFilteredProducts,
            minPrice, // Global minPrice across all products
            maxPrice, // Global maxPrice across all products
        };

        // Send Response
        res.json(response);
    } catch (error) {
        console.error('Error fetching products:', error);
        res.status(500).json({ error: 'Internal server error' });
    } finally {
        // Optional: Any cleanup operations can be performed here
        // For example, monitoring memory usage or logging request completion
    }
};


export const getProductById = async (req: Request, res: Response) => {
    const { id } = req.params;
    try {
        const productQuery = `
            SELECT 
                p.id AS product_id,
                p.name AS product_name,
                p.description,
                p.brand_id,
                p.has_color,
                p.has_size,
                p.product_type,
                p.price AS product_price,
                p.qty AS product_qty,
                p.sold AS product_sold,
                p.created_at,
                p.updated_at,
                pc.category_id,
                sc.name AS category_name,
                cl.id AS color_id,
                cl.name AS color_name,
                cl.price AS color_price,
                cl.qty AS color_qty,
                cl.sold AS color_sold,
                s.id AS size_id,
                s.name AS size_name,
                s.price AS size_price,
                s.qty AS size_qty,
                s.sold AS size_sold,
                i.id AS image_id,
                i.url AS image_url
            FROM products p
            LEFT JOIN product_categories pc ON p.id = pc.product_id
            LEFT JOIN subcategories sc ON pc.category_id = sc.id
            LEFT JOIN colors cl ON p.id = cl.product_id
            LEFT JOIN sizes s ON (p.id = s.product_id AND (cl.id = s.color_id OR s.color_id IS NULL))
            LEFT JOIN images i ON (p.id = i.product_id AND (cl.id = i.color_id OR i.color_id IS NULL) AND (s.id = i.size_id OR i.size_id IS NULL))
            WHERE p.id = $1
            ORDER BY pc.category_id, cl.id, s.id, i.id
        `;

        const result = await query(productQuery, [id]);

        if (result.rows.length === 0) {
            return res.status(404).json({ error: 'Product not found' });
        }

        // Process the result to group related data
        let product: {
            sold: any;
            has_size: any;
            images: any[];
            description: any;
            created_at: any;
            colors: any[];
            brand_id: any;
            has_color: any;
            product_type: any;
            updated_at: any;
            sizes: any[];
            price: any;
            qty: any;
            name: any;
            id: number;
            categories: any[]
        };
        product = {
            id: result.rows[0].product_id,
            name: result.rows[0].product_name,
            description: result.rows[0].description,
            brand_id: result.rows[0].brand_id,
            has_color: result.rows[0].has_color,
            has_size: result.rows[0].has_size,
            product_type: result.rows[0].product_type,
            price: result.rows[0].product_price,
            qty: result.rows[0].product_qty,
            sold: result.rows[0].product_sold,
            created_at: result.rows[0].created_at,
            updated_at: result.rows[0].updated_at,
            categories: [],
            colors: [],
            sizes: [],
            images: []
        };

        for (const row of result.rows) {
            if (row.category_id && !product.categories.some(c => c.id === row.category_id)) {
                product.categories.push({
                    id: row.category_id,
                    name: row.category_name
                });
            }

            // Add color if not already added
            if (row.color_id && !product.colors.some(c => c.id === row.color_id)) {
                product.colors.push({
                    id: row.color_id,
                    name: row.color_name,
                    price: row.color_price,
                    qty: row.color_qty,
                    sold: row.color_sold,
                    images: [],
                    sizes: []
                });
            }

            // Add size if not already added
            if (row.size_id && !product.sizes.some(s => s.id === row.size_id)) {
                const newSize = {
                    id: row.size_id,
                    name: row.size_name,
                    price: row.size_price,
                    qty: row.size_qty,
                    sold: row.size_sold,
                    color_id: row.color_id,
                    // @ts-ignore
                    images: []
                };
                product.sizes.push(newSize);

                // Add size to the corresponding color
                if (row.color_id) {
                    const color = product.colors.find(c => c.id === row.color_id);
                    if (color && !color.sizes.some((s: { id: any; }) => s.id === row.size_id)) {
                        color.sizes.push(newSize);
                    }
                }
            }

            if (row.image_id && !product.images.some(i => i.id === row.image_id)) {
                const newImage = {
                    id: row.image_id,
                    url: row.image_url,
                    color_id: row.color_id,
                    size_id: row.size_id
                };
                product.images.push(newImage);

                if (row.color_id) {
                    const color = product.colors.find(c => c.id === row.color_id);
                    if (color && !color.images.includes(row.image_url)) {
                        color.images.push(row.image_url);
                    }
                }
                if (row.size_id) {
                    const size = product.sizes.find(s => s.id === row.size_id);
                    if (size && !size.images.includes(row.image_url)) {
                        size.images.push(row.image_url);
                    }
                }
            }
        }

        res.json({ product });
    } catch (error) {
        console.error('Error fetching product by ID:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
};
// Create a new product
export const createProduct = async (req: Request, res: Response) => {
    try {
        console.log("Received product data:", req.body); // Log incoming data
        await query('BEGIN');

        const productData: CreateProductInput = req.body;

        // Adjust price if necessary
        let adjustedPrice = productData.price;
        if (productData.price < 1000) {
            adjustedPrice = parseFloat((productData.price * 1000).toFixed(2));
        }

        console.log("Inserting product with adjusted price:", adjustedPrice);

        // Insert product
        const productResult = await query(
            `INSERT INTO products (name, description, brand_id, has_color, has_size, product_type, price, qty, sold, created_at, updated_at)
             VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
             RETURNING id, created_at, updated_at`,
            [
                productData.name,
                productData.description,
                productData.brand_id,
                Boolean(productData.has_color),
                Boolean(productData.has_size),
                productData.product_type,
                adjustedPrice,
                productData.qty
            ]
        );
        const productId = productResult.rows[0].id;
        console.log(`Inserted product ID: ${productId}`);

        // Insert category associations
        const categoryInsertPromises = productData.category_ids.map((categoryId: number) => {
            console.log(`Associating category ID ${categoryId} with product ID ${productId}`);
            return query(
                `INSERT INTO product_categories (product_id, category_id) 
                 VALUES ($1, $2)`,
                [productId, categoryId]
            );
        });
        await Promise.all(categoryInsertPromises);
        console.log('Inserted all category associations.');

        // Insert images
        const imagesInsertPromises = productData.images
            .filter((image: ImageInput) => image.url) // Ensure URL exists
            .map((image: ImageInput) => {
                console.log(`Inserting product image: ${image.url}`);
                return query(
                    `INSERT INTO images (product_id, url, color_id, size_id)
       VALUES ($1, $2, $3, $4)
       RETURNING id, color_id, size_id, url`,
                    [productId, image.url, image.color_id || null, image.size_id || null]
                );
            });
        const insertedImagesResults = await Promise.all(imagesInsertPromises);
        const insertedImages = insertedImagesResults.map((imgResult: any) => ({
            id: imgResult.rows[0].id,
            url: imgResult.rows[0].url,
            color_id: imgResult.rows[0].color_id,
            size_id: imgResult.rows[0].size_id
        }));
        console.log(`Inserted ${insertedImages.length} product images.`);

        // Handle colors and sizes based on product type
        const insertedColors: number[] = [];
        const insertedSizes: number[] = [];

        if (productData.has_color && productData.colors) {
            for (const color of productData.colors) {
                // Adjust color price if necessary
                let adjustedColorPrice = color.price;
                if (color.price < 1000) {
                    adjustedColorPrice = parseFloat((color.price * 1000).toFixed(2));
                }

                console.log(`Inserting color: ${color.name} with has_size: ${color.has_size}`);

                // Insert color with explicit boolean casting
                const colorResult = await query(
                    `INSERT INTO colors (product_id, name, price, qty, sold, has_size)
                     VALUES ($1, $2, $3, $4, 0, $5)
                     RETURNING id, has_size`,
                    [productId, color.name, adjustedColorPrice, color.qty, Boolean(color.has_size)]
                );
                const colorId = colorResult.rows[0].id;
                const colorHasSize = colorResult.rows[0].has_size;
                console.log(`Inserted color ID=${colorId}, has_size=${colorHasSize}`);

                insertedColors.push(colorId);

                // Insert color images
                const colorImagesInsertPromises = color.images.map((image: ImageInput) => {
                    console.log(`Inserting color image: ${image.url}`);
                    return query(
                        `INSERT INTO images (product_id, color_id, url, size_id)
                         VALUES ($1, $2, $3, $4)
                         RETURNING id, color_id, size_id, url`,
                        [productId, colorId, image.url, image.size_id || null]
                    );
                });
                const insertedColorImagesResults = await Promise.all(colorImagesInsertPromises);
                console.log(`Inserted ${insertedColorImagesResults.length} images for color ID=${colorId}`);

                if (color.has_size && color.sizes) {
                    for (const size of color.sizes) {
                        // Adjust size price if necessary
                        let adjustedSizePrice = size.price;
                        if (size.price < 1000) {
                            adjustedSizePrice = parseFloat((size.price * 1000).toFixed(2));
                        }

                        console.log(`Inserting size: ${size.name} with price: ${adjustedSizePrice}`);

                        // Insert size
                        const sizeResult = await query(
                            `INSERT INTO sizes (product_id, color_id, name, price, qty, sold)
                             VALUES ($1, $2, $3, $4, $5, 0)
                             RETURNING id`,
                            [productId, colorId, size.name, adjustedSizePrice, size.qty]
                        );
                        const sizeId = sizeResult.rows[0].id;
                        console.log(`Inserted size ID=${sizeId} for color ID=${colorId}`);
                        insertedSizes.push(sizeId);

                        // Insert size images
                        const sizeImagesInsertPromises = size.images.map((image: ImageInput) => {
                            console.log(`Inserting size image: ${image.url}`);
                            return query(
                                `INSERT INTO images (product_id, color_id, size_id, url)
                                 VALUES ($1, $2, $3, $4)
                                 RETURNING id, color_id, size_id, url`,
                                [productId, colorId, sizeId, image.url]
                            );
                        });
                        const insertedSizeImagesResults = await Promise.all(sizeImagesInsertPromises);
                        console.log(`Inserted ${insertedSizeImagesResults.length} images for size ID=${sizeId}`);
                    }
                }
            }
        } else if (productData.has_size && productData.sizes) {
            for (const size of productData.sizes) {
                // Adjust size price if necessary
                let adjustedSizePrice = size.price;
                if (size.price < 1000) {
                    adjustedSizePrice = parseFloat((size.price * 1000).toFixed(2));
                }

                console.log(`Inserting standalone size: ${size.name} with price: ${adjustedSizePrice}`);

                // Insert size
                const sizeResult = await query(
                    `INSERT INTO sizes (product_id, name, price, qty, sold)
                     VALUES ($1, $2, $3, $4, 0)
                     RETURNING id`,
                    [productId, size.name, adjustedSizePrice, size.qty]
                );
                const sizeId = sizeResult.rows[0].id;
                console.log(`Inserted standalone size ID=${sizeId}`);
                insertedSizes.push(sizeId);

                // Insert size images
                const sizeImagesInsertPromises = size.images.map((image: ImageInput) => {
                    console.log(`Inserting standalone size image: ${image.url}`);
                    return query(
                        `INSERT INTO images (product_id, size_id, url)
                         VALUES ($1, $2, $3)
                         RETURNING id, color_id, size_id, url`,
                        [productId, sizeId, image.url]
                    );
                });
                const insertedSizeImagesResults = await Promise.all(sizeImagesInsertPromises);
                console.log(`Inserted ${insertedSizeImagesResults.length} images for standalone size ID=${sizeId}`);
            }
        }

        // Construct the new product object as it would appear in the cache
        const newProduct: Product = {
            id: productId,
            name: productData.name,
            description: productData.description,
            brand_id: productData.brand_id,
            has_color: productData.has_color,
            has_size: productData.has_size,
            product_type: productData.product_type,
            price: adjustedPrice,
            qty: productData.qty,
            sold: 0,
            created_at: productResult.rows[0].created_at.toISOString(),
            updated_at: productResult.rows[0].updated_at.toISOString(),
            categories: productData.category_ids.map((cid: number) => ({
                id: cid,
                name: "", // Optional: Fetch and populate category names
                main_category_id: 0, // Optional: Set appropriately
                main_category_name: "" // Optional: Set appropriately
            })),
            colors: productData.has_color && productData.colors ? productData.colors.map((color, index) => ({
                id: insertedColors[index],
                name: color.name,
                price: color.price || 0,
                qty: color.qty,
                sold: 0,
                has_size: Boolean(color.has_size),
                sizes: Boolean(color.has_size) && color.sizes ? color.sizes.map((size, sIndex) => ({
                    id: insertedSizes[sIndex],
                    name: size.name,
                    price: size.price,
                    qty: size.qty,
                    sold: 0,
                    color_id: null, // Optional: Set if needed
                    images: size.images.map(img => img.url)
                })) : [],
                images: color.images.map(img => img.url)
            })) : [],
            sizes: productData.has_size && !productData.has_color && productData.sizes ? productData.sizes.map((size, index) => ({
                id: insertedSizes[index],
                name: size.name,
                price: size.price,
                qty: size.qty,
                sold: 0,
                color_id: null,
                images: size.images.map(img => img.url)
            })) : [],
            images: insertedImages
        };

        console.log("New product to cache:", newProduct); // Log new product

        // Update the cache
        addProductToCache(newProduct);

        await query('COMMIT');
        res.status(201).json({ message: 'Product added successfully!', productId });
    } catch (error) {
        await query('ROLLBACK');
        console.error('Error creating product:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
};

export const updateProduct = async (req: Request, res: Response) => {
    const { id } = req.params;
    const productId = parseInt(id, 10);
    const productData: UpdateProductInput = req.body;

    if (isNaN(productId)) {
        return res.status(400).json({ error: 'Invalid product ID' });
    }

    try {
        await query('BEGIN');

        let adjustedPrice = productData.price;
        if (productData.price < 1000) {
            adjustedPrice = parseFloat((productData.price * 1000).toFixed(2));
        }

        const existingProductResult = await query('SELECT sold FROM products WHERE id = $1', [productId]);
        const existingSold = existingProductResult.rows[0]?.sold || 0;

        const result = await query(
            `UPDATE products 
             SET name = $1, 
                 description = $2, 
                 brand_id = $3, 
                 has_color = $4, 
                 has_size = $5, 
                 product_type = $6, 
                 price = $7, 
                 qty = $8, 
                 updated_at = CURRENT_TIMESTAMP 
             WHERE id = $9 
             RETURNING *`,
            [
                productData.name,
                productData.description,
                productData.brand_id,
                productData.has_color,
                productData.has_size,
                productData.product_type,
                adjustedPrice,
                productData.qty,
                productId
            ]
        );

        if (result.rows.length === 0) {
            await query('ROLLBACK');
            return res.status(404).json({ error: 'Product not found' });
        }

        const updatedProduct = result.rows[0];

        await query('DELETE FROM product_categories WHERE product_id = $1', [productId]);

        const categoryInsertPromises = productData.category_ids.map((categoryId: number) =>
            query(
                `INSERT INTO product_categories (product_id, category_id) 
                 VALUES ($1, $2)`,
                [productId, categoryId]
            )
        );
        await Promise.all(categoryInsertPromises);




        if (productData.images && productData.images.length > 0) {  // Only delete and re-insert images if provided
            await query('DELETE FROM images WHERE product_id = $1', [productId]);
            const imagesInsertPromises = productData.images.map((image: ImageInput) =>
                query(
                    `INSERT INTO images (product_id, url, color_id, size_id)
                     VALUES ($1, $2, $3, $4)`,
                    [productId, image.url, image.color_id || null, image.size_id || null]
                )
            );
            await Promise.all(imagesInsertPromises);
        }



        // Colors and Sizes
        await query('DELETE FROM colors WHERE product_id = $1', [productId]); // Delete all existing colors
        await query('DELETE FROM sizes WHERE product_id = $1', [productId]); // Delete all existing sizes


        if (productData.has_color) {
            for (const color of productData.colors) {
                let adjustedColorPrice = color.price;
                if (color.price < 1000) {
                    adjustedColorPrice = parseFloat((color.price * 1000).toFixed(2));
                }

                const colorResult = await query(
                    `INSERT INTO colors (product_id, name, price, qty, sold, has_size) 
                     VALUES ($1, $2, $3, $4, $5, $6) RETURNING id`,
                    [productId, color.name, adjustedColorPrice, color.qty, color.sold || 0, color.has_size]
                );

                const colorId = colorResult.rows[0].id;

                if (color.has_size) {
                    for (const size of color.sizes) {

                        let adjustedSizePrice = size.price;
                        if (size.price < 1000) {
                            adjustedSizePrice = parseFloat((size.price * 1000).toFixed(2));
                        }

                        await query(
                            `INSERT INTO sizes (product_id, color_id, name, price, qty, sold) 
                             VALUES ($1, $2, $3, $4, $5, $6)`,
                            [productId, colorId, size.name, adjustedSizePrice, size.qty, size.sold || 0]
                        );
                    }
                }
            }
        } else if (productData.has_size) {

            for (const size of productData.sizes) {
                let adjustedSizePrice = size.price;

                if (size.price < 1000) {
                    adjustedSizePrice = parseFloat((size.price * 1000).toFixed(2));
                }

                await query(
                    `INSERT INTO sizes (product_id, name, price, qty, sold) 
                     VALUES ($1, $2, $3, $4, $5)`,
                    [productId, size.name, adjustedSizePrice, size.qty, size.sold || 0]
                );

            }
        }



        // Cache Update
        const productQuery = `
            SELECT *  FROM products WHERE id = $1
        `;
        const productResult = await query(productQuery, [productId]);
        const productRow = productResult.rows[0];


        const categoryDetails = await fetchCategoryDetails(productData.category_ids)

        const cachedProduct: Product = {
            id: productRow.id,
            name: productRow.name,
            description: productRow.description,
            brand_id: productRow.brand_id,
            has_color: productRow.has_color,
            has_size: productRow.has_size,
            product_type: productRow.product_type,
            price: productRow.price,
            qty: productRow.qty,
            sold: existingSold,
            created_at: productRow.created_at,
            updated_at: productRow.updated_at,
            categories: categoryDetails,
            colors: [],
            sizes: [],
            images: [] // Initialize as empty, will be populated below
        };

        const imagesResult = await query('SELECT * FROM images WHERE product_id = $1', [productId]);
        cachedProduct.images = imagesResult.rows.map((row: any): Image => ({ // Explicitly type as Image[]
            id: row.id,
            url: row.url,
            color_id: row.color_id,
            size_id: row.size_id,
        }));

        if (cachedProduct.has_color) {
            const colorsResult = await query('SELECT * FROM colors WHERE product_id = $1', [productId]);

            cachedProduct.colors = await Promise.all(colorsResult.rows.map(async (colorRow: any) => {
                const color: Color = {
                    id: colorRow.id,
                    name: colorRow.name,
                    price: colorRow.price,
                    qty: colorRow.qty,
                    sold: colorRow.sold,
                    has_size: colorRow.has_size,
                    images: cachedProduct.images.filter((img) => img.color_id === colorRow.id).map(img => img.url),
                    sizes: []
                };

                if (color.has_size) { // Using color.has_size
                    const sizesResult = await query('SELECT * FROM sizes WHERE color_id = $1', [colorRow.id]);
                    color.sizes = sizesResult.rows.map((sizeRow: any): Size => ({
                        id: sizeRow.id,
                        name: sizeRow.name,
                        price: sizeRow.price,
                        qty: sizeRow.qty,
                        sold: sizeRow.sold,
                        color_id: sizeRow.color_id,
                        images: cachedProduct.images.filter((img) => img.size_id === sizeRow.id).map(img => img.url),
                    }));
                }

                return color;
            }));
        }

        if (cachedProduct.has_size && !cachedProduct.has_color) {
            const sizesResult = await query('SELECT * FROM sizes WHERE product_id = $1 AND color_id IS NULL', [productId]);
            cachedProduct.sizes = sizesResult.rows.map((sizeRow: any): Size => ({
                id: sizeRow.id,
                name: sizeRow.name,
                price: sizeRow.price,
                qty: sizeRow.qty,
                sold: sizeRow.sold,
                color_id: null,
                images: cachedProduct.images.filter((img) => img.size_id === sizeRow.id).map(img => img.url),
            }));
        } else {
            cachedProduct.sizes = []; // Important: Initialize to empty array if no standalone sizes
        }



        updateProductInCache(cachedProduct);


        await query('COMMIT');
        res.json({ message: 'Product updated successfully', product: updatedProduct });
    } catch (error) {
        await query('ROLLBACK');
        console.error('Error updating product:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
};
export const getTopSellingProducts = async (req: Request, res: Response): Promise<void> => {
    try {
        // Retrieve cached products
        let cacheData: CacheData | undefined = productCache.get('allProducts');

        if (!cacheData) {
            // Cache miss: Load from DB and cache
            cacheData = await loadAndCacheAllProducts();
            console.log('Loaded products from database and cached them.');
        } else {
            console.log('Loaded products from cache.');
        }

        const { allProducts } = cacheData;

        // Function to compute minimum price excluding 0
        const getMinPrice = (product: Product): number | null => {
            let prices: number[] = [];

            // Include product base price if >0
            if (product.price > 0) prices.push(product.price);

            // Include colors' prices
            if (product.has_color) {
                product.colors.forEach(color => {
                    if (color.price > 0) prices.push(color.price);

                    // Include sizes' prices within colors
                    if (color.has_size) {
                        color.sizes.forEach(size => {
                            if (size.price > 0) prices.push(size.price);
                        });
                    }
                });
            }

            // Include standalone sizes' prices
            if (product.has_size) {
                product.sizes.forEach(size => {
                    if (size.price > 0) prices.push(size.price);
                });
            }

            if (prices.length === 0) return null;

            return Math.min(...prices);
        };

        // Function to get one image URL (first available)
        const getOneImage = (product: Product): string | null => {
            if (product.images && product.images.length > 0) {
                return product.images[0].url; // Assuming each image has a 'url' property
            }
            return null; // Or return a placeholder image URL if desired
        };

        // Filter products that are in stock and have a valid min price
        const filteredProducts = allProducts
            .map(product => {
                // Determine if the product is in stock
                const isInStock = product.qty > 0 ||
                    (product.has_color && product.colors.some(color => color.qty > 0 ||
                        (color.has_size && color.sizes.some(size => size.qty > 0)))) ||
                    (product.has_size && product.sizes.some(size => size.qty > 0));

                // Calculate min price
                const minPrice = getMinPrice(product);

                // Get one image
                const image = getOneImage(product);

                return {
                    id: product.id,
                    title: product.name,
                    description: product.description,
                    sold: Math.abs(product.sold), // Ensure sold is absolute
                    isInStock,
                    minPrice,
                    image
                };
            })
            .filter(product => product.isInStock && product.minPrice !== null && product.minPrice > 0);

        if (filteredProducts.length === 0) {
             res.status(200).json({ products: [] });
        }

        // Sort the products by sold in descending order using absolute sold values
        filteredProducts.sort((a, b) => b.sold - a.sold);

        // Select the top two products
        const topTwoProducts = filteredProducts.slice(0, 2).map(product => ({
            id: product.id,
            title: product.title,
            description: product.description,
            minPrice: product.minPrice as number,
            image: product.image
        }));

        res.status(200).json({ products: topTwoProducts });
    } catch (error) {
        console.error('Error fetching top selling products:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
};


export const deleteProduct = async (req: Request, res: Response) => {
    const { id } = req.params;
    const productId: number = parseInt(id, 10);

    if (isNaN(productId)) {
        return res.status(400).json({ error: 'Invalid product ID' });
    }

    try {
        await query('BEGIN');

        // Delete related data first
        await query('DELETE FROM product_categories WHERE product_id = $1', [productId]);
        await query('DELETE FROM images WHERE product_id = $1', [productId]);
        await query('DELETE FROM sizes WHERE product_id = $1', [productId]);
        await query('DELETE FROM colors WHERE product_id = $1', [productId]);

        // Delete the product
        const result = await query('DELETE FROM products WHERE id = $1 RETURNING *', [productId]);

        if (result.rows.length === 0) {
            await query('ROLLBACK');
            return res.status(404).json({ error: 'Product not found' });
        }

        // Delete the product from the cache
        deleteProductFromCache(productId);

        await query('COMMIT');
        res.json({ message: 'Product and related data deleted successfully' });
    } catch (error) {
        await query('ROLLBACK');
        console.error('Error deleting product:', error);
        res.status(500).json({ error: 'Internal server error' });
    }
};
cacheHelper.ts:
// src/cacheHelpers.ts

import { productCache } from "./cache";
import { Product, CacheData, Color, Size } from "product"; // Adjust the import path as necessary

/**
 * Adds a new product to the cache.
 */
export const addProductToCache = (newProduct: Product) => {
    try {
        const cacheData: CacheData | undefined = productCache.get('allProducts');
        if (cacheData) {
            cacheData.allProducts.push(newProduct);

            // Extract all prices
            const productPrices: number[] = [];
            productPrices.push(newProduct.price);

            newProduct.colors.forEach((color: Color) => {
                productPrices.push(color.price);
                color.sizes.forEach((size: Size) => productPrices.push(size.price));
            });

            newProduct.sizes.forEach((size: Size) => productPrices.push(size.price));

            // Update minPrice and maxPrice
            if (productPrices.length > 0) {
                const productMinPrice = Math.min(...productPrices, cacheData.minPrice);
                const productMaxPrice = Math.max(...productPrices, cacheData.maxPrice);
                cacheData.minPrice = Math.min(cacheData.minPrice, productMinPrice);
                cacheData.maxPrice = Math.max(cacheData.maxPrice, productMaxPrice);
            }

            productCache.set('allProducts', cacheData);
            console.log('Added new product to cache.');
        } else {
            // Flush cache to force reload on next access
            productCache.del('allProducts');
            console.log('Cache unavailable. Flushed product cache.');
        }
    } catch (error) {
        console.error('Error adding product to cache:', error);
        productCache.del('allProducts');
    }
};

/**
 * Updates an existing product in the cache.
 */
export const updateProductInCache = (updatedProduct: Product) => {
    try {
        const cacheData: CacheData | undefined = productCache.get('allProducts');
        if (cacheData) {
            const index = cacheData.allProducts.findIndex(p => p.id === updatedProduct.id);
            if (index !== -1) {
                cacheData.allProducts[index] = updatedProduct;

                // Recalculate minPrice and maxPrice
                const allPrices: number[] = [];
                cacheData.allProducts.forEach(product => {
                    allPrices.push(product.price);
                    product.colors.forEach(color => {
                        allPrices.push(color.price);
                        color.sizes.forEach(size => allPrices.push(size.price));
                    });
                    product.sizes.forEach(size => allPrices.push(size.price));
                });

                if (allPrices.length > 0) {
                    cacheData.minPrice = Math.min(...allPrices);
                    cacheData.maxPrice = Math.max(...allPrices);
                } else {
                    cacheData.minPrice = 0;
                    cacheData.maxPrice = 0;
                }

                productCache.set('allProducts', cacheData);
                console.log('Updated product in cache.');
            } else {
                // Product not found; flush cache
                productCache.del('allProducts');
                console.log('Product not found in cache. Flushed product cache.');
            }
        } else {
            // Flush cache to force reload
            productCache.del('allProducts');
            console.log('Cache unavailable. Flushed product cache.');
        }
    } catch (error) {
        console.error('Error updating product in cache:', error);
        productCache.del('allProducts');
    }
};

/**
 * Deletes a product from the cache.
 */
export const deleteProductFromCache = (productId: number) => {
    try {
        const cacheData: CacheData | undefined = productCache.get('allProducts');
        if (cacheData) {
            const initialLength = cacheData.allProducts.length;
            cacheData.allProducts = cacheData.allProducts.filter(p => p.id !== productId);
            if (cacheData.allProducts.length < initialLength) {
                // Recalculate minPrice and maxPrice
                const allPrices: number[] = [];
                cacheData.allProducts.forEach(product => {
                    allPrices.push(product.price);
                    product.colors.forEach(color => {
                        allPrices.push(color.price);
                        color.sizes.forEach(size => allPrices.push(size.price));
                    });
                    product.sizes.forEach(size => allPrices.push(size.price));
                });

                if (allPrices.length > 0) {
                    cacheData.minPrice = Math.min(...allPrices);
                    cacheData.maxPrice = Math.max(...allPrices);
                } else {
                    cacheData.minPrice = 0;
                    cacheData.maxPrice = 0;
                }

                productCache.set('allProducts', cacheData);
                console.log('Deleted product from cache.');
            } else {
                // Product not found; flush cache
                productCache.del('allProducts');
                console.log('Product not found in cache. Flushed product cache.');
            }
        } else {
            // Flush cache to force reload
            productCache.del('allProducts');
            console.log('Cache unavailable. Flushed product cache.');
        }
    } catch (error) {
        console.error('Error deleting product from cache:', error);
        productCache.del('allProducts');
    }
};

/**
 * Decreases product, size, or color quantity in the cache based on identifiers.
 * @param productId - ID of the product
 * @param sizeId - ID of the size (optional)
 * @param colorId - ID of the color (optional)
 * @param quantity - Quantity to decrease
 */
export const decreaseProductQtyInCache = (productId: number, sizeId: number | null, colorId: number | null, quantity: number) => {
    try {
        const cacheData: CacheData | undefined = productCache.get('allProducts');
        if (!cacheData) {
            // Cache is unavailable; nothing to update
            return;
        }

        const product = cacheData.allProducts.find(p => p.id === productId);
        if (!product) {
            // Product not found in cache; nothing to update
            return;
        }

        if (sizeId) {
            // Decrease quantity in the specific size
            const size = product.sizes.find(s => s.id === sizeId);
            if (size) {
                size.qty = size.qty - quantity;
                if (size.qty < 0) size.qty = 0; // Prevent negative stock
            }

            // Also, if color_id is present, update the associated color's size
            if (colorId) {
                const color = product.colors.find(c => c.id === colorId);
                if (color) {
                    const colorSize = color.sizes.find(s => s.id === sizeId);
                    if (colorSize) {
                        colorSize.qty = colorSize.qty - quantity;
                        if (colorSize.qty < 0) colorSize.qty = 0;
                    }
                }
            }
        } else if (colorId) {
            // Decrease quantity in the specific color
            const color = product.colors.find(c => c.id === colorId);
            if (color) {
                color.qty = color.qty - quantity;
                if (color.qty < 0) color.qty = 0; // Prevent negative stock
            }
        } else {
            // Decrease quantity in the product itself
            product.qty = product.qty - quantity;
            if (product.qty < 0) product.qty = 0; // Prevent negative stock
        }

        // Update the cache
        productCache.set('allProducts', cacheData);
        console.log('Decreased product quantity in cache.');
    } catch (error) {
        console.error('Error decreasing product quantity in cache:', error);
        // Decide whether to flush the cache or handle the error differently
        productCache.del('allProducts');
    }
};

/**
 * Increases the 'sold' count for a size in the cache.
 */
export const increaseSizeSoldInCache = (productId: number, sizeId: number, quantity: number): void => {
    try {
        const cacheData: CacheData | undefined = productCache.get('allProducts');
        if (!cacheData) {
            // Cache is unavailable; nothing to update
            return;
        }

        const product = cacheData.allProducts.find(p => p.id === productId);
        if (!product) {
            // Product not found in cache; nothing to update
            return;
        }

        const size = product.sizes.find(s => s.id === sizeId);
        if (size) {
            size.sold += quantity;
            // Ensure cache is updated
            productCache.set('allProducts', cacheData);
            console.log(`Increased sold count for size ID ${sizeId} in cache.`);
        }
    } catch (error) {
        console.error('Error increasing size sold in cache:', error);
        // Optionally, decide to flush the cache or handle the error differently
        productCache.del('allProducts');
    }
};

/**
 * Increases the 'sold' count for a color in the cache.
 */
export const increaseColorSoldInCache = (productId: number, colorId: number, quantity: number): void => {
    try {
        const cacheData: CacheData | undefined = productCache.get('allProducts');
        if (!cacheData) {
            // Cache is unavailable; nothing to update
            return;
        }

        const product = cacheData.allProducts.find(p => p.id === productId);
        if (!product) {
            // Product not found in cache; nothing to update
            return;
        }

        const color = product.colors.find(c => c.id === colorId);
        if (color) {
            color.sold += quantity;
            // Ensure cache is updated
            productCache.set('allProducts', cacheData);
            console.log(`Increased sold count for color ID ${colorId} in cache.`);
        }
    } catch (error) {
        console.error('Error increasing color sold in cache:', error);
        // Optionally, decide to flush the cache or handle the error differently
        productCache.del('allProducts');
    }
};

/**
 * Increases the 'sold' count for a product in the cache.
 */
export const increaseProductSoldInCache = (productId: number, quantity: number): void => {
    try {
        const cacheData: CacheData | undefined = productCache.get('allProducts');
        if (!cacheData) {
            // Cache is unavailable; nothing to update
            return;
        }

        const product = cacheData.allProducts.find(p => p.id === productId);
        if (product) {
            product.sold += quantity;
            // Ensure cache is updated
            productCache.set('allProducts', cacheData);
            console.log(`Increased sold count for product ID ${productId} in cache.`);
        }
    } catch (error) {
        console.error('Error increasing product sold in cache:', error);
        // Optionally, decide to flush the cache or handle the error differently
        productCache.del('allProducts');
    }
};
product.ts:
// src/types/productInput.ts

export interface ImageInput {
    url: string;
    color_id?: number | null;
    size_id?: number | null;
}

export interface SizeInput {
    id?: number; // Optional for creation
    name: string;
    price: number; // Changed from string to number
    qty: number;
    sold?: number;
    color_id?: number | null;
    images: ImageInput[];
}

export interface ColorInput {
    id?: number; // Optional for creation
    name: string;
    price: number; // Changed from string to number
    qty: number;
    sold?: number;
    has_size: boolean;
    sizes: SizeInput[];
    images: ImageInput[];
}

export interface CreateProductInput {
    name: string;
    description: string;
    brand_id: number;
    has_color: boolean;
    has_size: boolean;
    product_type: string;
    price: number; // Changed from string to number
    qty: number;
    category_ids: number[];
    images: ImageInput[];
    colors?: ColorInput[]; // Optional based on has_color
    sizes?: SizeInput[]; // Optional based on has_size and has_color
}

export interface UpdateProductInput extends CreateProductInput {}

export interface Category {
    id: number;
    name: string;
    main_category_id: number;
    main_category_name: string;
}

export interface Image {
    id: number;
    url: string;
    color_id: number | null;
    size_id: number | null;
}

export interface Size {
    id: number;
    name: string;
    price: number; // Changed from string to number for arithmetic operations
    qty: number;
    sold: number;
    color_id: number | null;
    images: string[];
}

export interface Color {
    id: number;
    name: string;
    price: number; // Changed from string to number
    qty: number;
    sold: number;
    has_size: boolean; // Mandatory property
    sizes: Size[];
    images: string[];
}

export interface Product {
    id: number;
    name: string;
    description: string;
    brand_id: number; // Also a subcategory_id for brands
    has_color: boolean;
    has_size: boolean;
    product_type: string;
    price: number; // Changed from string to number
    qty: number;
    sold: number;
    created_at: string;
    updated_at: string;
    categories: Category[];
    colors: Color[];
    sizes: Size[];
    images: Image[];
}

export interface Brand {
    id: number;
    name: string;
    category_id: number;
    category_name: string;
}

export interface CacheData {
    allProducts: Product[];
    minPrice: number;
    maxPrice: number;
}

export interface BrandsCacheData {
    allBrands: Brand[];
}

product.ts may not be correct 100% though...