<cfscript>
// Save menu data from the builder UI (OPTIMIZED)
// Input: BusinessID, Menu (JSON structure)
// Output: { OK: true }
// VERSION: 2026-02-08-fix1

response = { "OK": false, "VERSION": "2026-02-08-fix2", "DEBUG": [] };

// Track which templates we've already saved options for (to avoid duplicate saves)
savedTemplates = {};

// Recursive function to save options/modifiers at any depth
function saveOptionsRecursive(options, parentID, businessID) {
    if (!isArray(options) || arrayLen(options) == 0) return;

    var optSortOrder = 0;
    for (var opt in options) {
        var optDbId = structKeyExists(opt, "dbId") ? val(opt.dbId) : 0;
        var requiresSelection = (structKeyExists(opt, "requiresSelection") && opt.requiresSelection) ? 1 : 0;
        var maxSelections = structKeyExists(opt, "maxSelections") ? val(opt.maxSelections) : 0;
        var isDefault = (structKeyExists(opt, "isDefault") && opt.isDefault) ? 1 : 0;
        var optionID = 0;

        if (optDbId > 0) {
            optionID = optDbId;
            queryTimed("
                UPDATE Items
                SET Name = :name,
                    Price = :price,
                    IsCheckedByDefault = :isDefault,
                    SortOrder = :sortOrder,
                    RequiresChildSelection = :requiresSelection,
                    MaxNumSelectionReq = :maxSelections,
                    ParentItemID = :parentID
                WHERE ID = :optID
            ", {
                optID: optDbId,
                parentID: parentID,
                name: opt.name,
                price: val(opt.price ?: 0),
                isDefault: isDefault,
                sortOrder: optSortOrder,
                requiresSelection: requiresSelection,
                maxSelections: maxSelections
            });
        } else {
            queryTimed("
                INSERT INTO Items (
                    BusinessID, ParentItemID, Name, Price,
                    IsCheckedByDefault, SortOrder, IsActive, AddedOn,
                    RequiresChildSelection, MaxNumSelectionReq, CategoryID
                ) VALUES (
                    :businessID, :parentID, :name, :price,
                    :isDefault, :sortOrder, 1, NOW(),
                    :requiresSelection, :maxSelections, 0
                )
            ", {
                businessID: businessID,
                parentID: parentID,
                name: opt.name,
                price: val(opt.price ?: 0),
                isDefault: isDefault,
                sortOrder: optSortOrder,
                requiresSelection: requiresSelection,
                maxSelections: maxSelections
            });

            var result = queryTimed("SELECT LAST_INSERT_ID() as newID");
            optionID = result.newID;
        }

        if (structKeyExists(opt, "options") && isArray(opt.options) && arrayLen(opt.options) > 0) {
            saveOptionsRecursive(opt.options, optionID, businessID);
        }

        optSortOrder++;
    }
}

try {
    requestBody = toString(getHttpRequestData().content);

    if (!len(requestBody)) {
        throw("Request body is required");
    }

    jsonData = deserializeJSON(requestBody);
    businessID = val(jsonData.BusinessID ?: 0);
    menu = jsonData.Menu ?: {};

    if (businessID == 0) {
        throw("BusinessID is required");
    }

    if (!structKeyExists(menu, "categories") || !isArray(menu.categories)) {
        throw("Menu categories are required");
    }

    // Check if Categories table has data (must match getForBuilder logic!)
    // If Categories table has data, use legacy schema (CategoryID in Items)
    // Otherwise use unified schema (ParentItemID in Items)
    hasCategoriesData = false;
    try {
        qCatCheck = queryTimed("
            SELECT 1 FROM Categories
            WHERE BusinessID = :businessID
            LIMIT 1
        ", { businessID: businessID }, { datasource: "payfrit" });
        hasCategoriesData = (qCatCheck.recordCount > 0);
    } catch (any e) {
        hasCategoriesData = false;
    }
    newSchemaActive = !hasCategoriesData;

    // Wrap everything in a transaction for speed and consistency
    transaction {
        catSortOrder = 0;
        for (cat in menu.categories) {
            categoryID = 0;
            categoryDbId = structKeyExists(cat, "dbId") ? val(cat.dbId) : 0;
            catItemCount = structKeyExists(cat, "items") && isArray(cat.items) ? arrayLen(cat.items) : 0;

            // Debug: log each category being processed
            arrayAppend(response.DEBUG, "Category: " & cat.name & " (dbId=" & categoryDbId & ") with " & catItemCount & " items");

            // Initialize menuId param - use 0 for "no menu" (nullable in DB)
            categoryMenuId = structKeyExists(cat, "menuId") ? val(cat.menuId) : 0;

            if (newSchemaActive) {
                if (categoryDbId > 0) {
                    categoryID = categoryDbId;
                    queryTimed("
                        UPDATE Items
                        SET Name = :name,
                            SortOrder = :sortOrder
                        WHERE ID = :categoryID
                          AND BusinessID = :businessID
                    ", {
                        categoryID: categoryID,
                        businessID: businessID,
                        name: cat.name,
                        sortOrder: catSortOrder
                    });
                } else {
                    queryTimed("
                        INSERT INTO Items (
                            BusinessID, Name, Description,
                            ParentItemID, Price, IsActive,
                            SortOrder, AddedOn, CategoryID
                        ) VALUES (
                            :businessID, :name, '',
                            0, 0, 1,
                            :sortOrder, NOW(), 0
                        )
                    ", {
                        businessID: businessID,
                        name: cat.name,
                        sortOrder: catSortOrder
                    });

                    result = queryTimed("SELECT LAST_INSERT_ID() as newID");
                    categoryID = result.newID;
                }
            } else {
                if (categoryDbId > 0) {
                    categoryID = categoryDbId;
                    queryTimed("
                        UPDATE Categories
                        SET Name = :name,
                            SortOrder = :sortOrder,
                            MenuID = NULLIF(:menuId, 0)
                        WHERE ID = :categoryID
                    ", {
                        categoryID: categoryID,
                        name: cat.name,
                        sortOrder: catSortOrder,
                        menuId: categoryMenuId
                    });
                } else {
                    queryTimed("
                        INSERT INTO Categories (BusinessID, MenuID, Name, SortOrder, AddedOn)
                        VALUES (:businessID, NULLIF(:menuId, 0), :name, :sortOrder, NOW())
                    ", {
                        businessID: businessID,
                        menuId: categoryMenuId,
                        name: cat.name,
                        sortOrder: catSortOrder
                    });

                    result = queryTimed("SELECT LAST_INSERT_ID() as newID");
                    categoryID = result.newID;
                }
            }

            // Debug: log final categoryID for this category
            arrayAppend(response.DEBUG, "  -> CategoryID resolved to: " & categoryID);

            // Process items
            if (structKeyExists(cat, "items") && isArray(cat.items)) {
                itemSortOrder = 0;
                for (item in cat.items) {
                    itemID = 0;
                    itemDbId = structKeyExists(item, "dbId") ? val(item.dbId) : 0;

                    // Debug: log each item being processed
                    arrayAppend(response.DEBUG, "    Item: " & item.name & " (dbId=" & itemDbId & ") -> CategoryID=" & categoryID);

                    if (itemDbId > 0) {
                        itemID = itemDbId;

                        if (newSchemaActive) {
                            queryTimed("
                                UPDATE Items
                                SET Name = :name,
                                    Description = :description,
                                    Price = :price,
                                    ParentItemID = :categoryID,
                                    SortOrder = :sortOrder
                                WHERE ID = :itemID
                            ", {
                                itemID: itemID,
                                name: item.name,
                                description: item.description ?: "",
                                price: val(item.price ?: 0),
                                categoryID: categoryID,
                                sortOrder: itemSortOrder
                            });
                        } else {
                            queryTimed("
                                UPDATE Items
                                SET Name = :name,
                                    Description = :description,
                                    Price = :price,
                                    CategoryID = :categoryID,
                                    SortOrder = :sortOrder
                                WHERE ID = :itemID
                            ", {
                                itemID: itemID,
                                name: item.name,
                                description: item.description ?: "",
                                price: val(item.price ?: 0),
                                categoryID: categoryID,
                                sortOrder: itemSortOrder
                            });
                        }
                    } else {
                        if (newSchemaActive) {
                            queryTimed("
                                INSERT INTO Items (
                                    BusinessID, ParentItemID, Name, Description,
                                    Price, SortOrder, IsActive, AddedOn, CategoryID
                                ) VALUES (
                                    :businessID, :categoryID, :name, :description,
                                    :price, :sortOrder, 1, NOW(), 0
                                )
                            ", {
                                businessID: businessID,
                                categoryID: categoryID,
                                name: item.name,
                                description: item.description ?: "",
                                price: val(item.price ?: 0),
                                sortOrder: itemSortOrder
                            });
                        } else {
                            queryTimed("
                                INSERT INTO Items (
                                    BusinessID, CategoryID, Name, Description,
                                    Price, SortOrder, IsActive, ParentItemID, AddedOn
                                ) VALUES (
                                    :businessID, :categoryID, :name, :description,
                                    :price, :sortOrder, 1, 0, NOW()
                                )
                            ", {
                                businessID: businessID,
                                categoryID: categoryID,
                                name: item.name,
                                description: item.description ?: "",
                                price: val(item.price ?: 0),
                                sortOrder: itemSortOrder
                            });
                        }

                        result = queryTimed("SELECT LAST_INSERT_ID() as newID");
                        itemID = result.newID;
                    }

                    // Handle modifiers
                    if (structKeyExists(item, "modifiers") && isArray(item.modifiers) && arrayLen(item.modifiers) > 0) {
                        // Clear existing template links for this item
                        queryTimed("DELETE FROM lt_ItemID_TemplateItemID WHERE ItemID = :itemID", { itemID: itemID });

                        modSortOrder = 0;
                        for (mod in item.modifiers) {
                            modDbId = structKeyExists(mod, "dbId") ? val(mod.dbId) : 0;
                            requiresSelection = (structKeyExists(mod, "requiresSelection") && mod.requiresSelection) ? 1 : 0;
                            maxSelections = structKeyExists(mod, "maxSelections") ? val(mod.maxSelections) : 0;

                            if (structKeyExists(mod, "isTemplate") && mod.isTemplate && modDbId > 0) {
                                // This is a template reference - create link
                                queryTimed("
                                    INSERT INTO lt_ItemID_TemplateItemID (ItemID, TemplateItemID, SortOrder)
                                    VALUES (:itemID, :templateID, :sortOrder)
                                    ON DUPLICATE KEY UPDATE SortOrder = :sortOrder
                                ", {
                                    itemID: itemID,
                                    templateID: modDbId,
                                    sortOrder: modSortOrder
                                });

                                // Update template's selection rules
                                queryTimed("
                                    UPDATE Items
                                    SET RequiresChildSelection = :requiresSelection,
                                        MaxNumSelectionReq = :maxSelections
                                    WHERE ID = :modID
                                ", {
                                    modID: modDbId,
                                    requiresSelection: requiresSelection,
                                    maxSelections: maxSelections
                                });

                                // Only save template options ONCE (first time we encounter this template)
                                if (!structKeyExists(savedTemplates, modDbId)) {
                                    savedTemplates[modDbId] = true;
                                    if (structKeyExists(mod, "options") && isArray(mod.options)) {
                                        saveOptionsRecursive(mod.options, modDbId, businessID);
                                    }
                                }
                            } else if (modDbId > 0) {
                                // Direct modifier (not a template) - update it
                                queryTimed("
                                    UPDATE Items
                                    SET Name = :name,
                                        Price = :price,
                                        IsCheckedByDefault = :isDefault,
                                        SortOrder = :sortOrder,
                                        RequiresChildSelection = :requiresSelection,
                                        MaxNumSelectionReq = :maxSelections,
                                        ParentItemID = :parentID
                                    WHERE ID = :modID
                                ", {
                                    modID: modDbId,
                                    parentID: itemID,
                                    name: mod.name,
                                    price: val(mod.price ?: 0),
                                    isDefault: (mod.isDefault ?: false) ? 1 : 0,
                                    sortOrder: modSortOrder,
                                    requiresSelection: requiresSelection,
                                    maxSelections: maxSelections
                                });

                                if (structKeyExists(mod, "options") && isArray(mod.options)) {
                                    saveOptionsRecursive(mod.options, modDbId, businessID);
                                }
                            } else {
                                // New direct modifier - insert it
                                queryTimed("
                                    INSERT INTO Items (
                                        BusinessID, ParentItemID, Name, Price,
                                        IsCheckedByDefault, SortOrder, IsActive, AddedOn,
                                        RequiresChildSelection, MaxNumSelectionReq, CategoryID
                                    ) VALUES (
                                        :businessID, :parentID, :name, :price,
                                        :isDefault, :sortOrder, 1, NOW(),
                                        :requiresSelection, :maxSelections, 0
                                    )
                                ", {
                                    businessID: businessID,
                                    parentID: itemID,
                                    name: mod.name,
                                    price: val(mod.price ?: 0),
                                    isDefault: (mod.isDefault ?: false) ? 1 : 0,
                                    sortOrder: modSortOrder,
                                    requiresSelection: requiresSelection,
                                    maxSelections: maxSelections
                                });

                                modResult = queryTimed("SELECT LAST_INSERT_ID() as newModID");
                                newModID = modResult.newModID;
                                if (structKeyExists(mod, "options") && isArray(mod.options)) {
                                    saveOptionsRecursive(mod.options, newModID, businessID);
                                }
                            }
                            modSortOrder++;
                        }
                    }
                    itemSortOrder++;
                }
            }
            catSortOrder++;
        }
    }

    response = { "OK": true, "SCHEMA": newSchemaActive ? "unified" : "legacy" };

} catch (any e) {
    response = {
        "OK": false,
        "ERROR": e.message,
        "DETAIL": e.detail ?: "",
        "TYPE": e.type ?: ""
    };
}

cfheader(name="Content-Type", value="application/json");
writeOutput(serializeJSON(response));
</cfscript>
