<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfheader name="Cache-Control" value="no-store">

<cfscript>
/**
 * Get Menu for Builder
 * Returns categories and items in structured format for the menu builder UI
 */

response = { "OK": false };

// Recursive function to build nested options
function buildOptionsTree(allOptions, parentId) {
    var result = [];
    for (var i = 1; i <= allOptions.recordCount; i++) {
        if (allOptions.ParentItemID[i] == parentId) {
            var children = buildOptionsTree(allOptions, allOptions.ItemID[i]);
            arrayAppend(result, {
                "id": "opt_" & allOptions.ItemID[i],
                "dbId": allOptions.ItemID[i],
                "name": allOptions.Name[i],
                "price": allOptions.Price[i],
                "isDefault": allOptions.IsDefault[i] == 1 ? true : false,
                "sortOrder": allOptions.SortOrder[i],
                "requiresSelection": isNull(allOptions.RequiresSelection[i]) ? false : (allOptions.RequiresSelection[i] == 1),
                "maxSelections": isNull(allOptions.MaxSelections[i]) ? 0 : allOptions.MaxSelections[i],
                "options": children
            });
        }
    }
    if (arrayLen(result) > 1) {
        arraySort(result, function(a, b) {
            return a.sortOrder - b.sortOrder;
        });
    }
    return result;
}

try {
    requestBody = toString(getHttpRequestData().content);
    requestData = {};
    if (len(requestBody)) {
        requestData = deserializeJSON(requestBody);
    }

    businessID = 0;
    if (structKeyExists(requestData, "BusinessID")) {
        businessID = val(requestData.BusinessID);
    }

    if (businessID == 0) {
        response["ERROR"] = "missing_business_id";
        response["MESSAGE"] = "BusinessID is required";
        writeOutput(serializeJSON(response));
        abort;
    }

    // Check for MenuID filter (optional - if provided, only return categories for that menu)
    menuID = structKeyExists(requestData, "MenuID") ? val(requestData.MenuID) : 0;

    // Get business's default menu setting
    defaultMenuID = 0;
    try {
        qBiz = queryTimed("SELECT DefaultMenuID FROM Businesses WHERE ID = :businessID",
            { businessID: businessID }, { datasource: "payfrit" });
        if (qBiz.recordCount > 0 && !isNull(qBiz.DefaultMenuID)) {
            defaultMenuID = val(qBiz.DefaultMenuID);
        }
    } catch (any e) {}

    // Get all menus for this business
    allMenus = [];
    try {
        qMenus = queryTimed("
            SELECT ID, Name, Description, DaysActive,
                   StartTime, EndTime, SortOrder
            FROM Menus
            WHERE BusinessID = :businessID AND IsActive = 1
            ORDER BY SortOrder, Name
        ", { businessID: businessID }, { datasource: "payfrit" });

        for (m = 1; m <= qMenus.recordCount; m++) {
            arrayAppend(allMenus, {
                "MenuID": qMenus.ID[m],
                "MenuName": qMenus.Name[m],
                "MenuDescription": isNull(qMenus.Description[m]) ? "" : qMenus.Description[m],
                "MenuDaysActive": qMenus.DaysActive[m],
                "MenuStartTime": isNull(qMenus.StartTime[m]) ? "" : timeFormat(qMenus.StartTime[m], "HH:mm"),
                "MenuEndTime": isNull(qMenus.EndTime[m]) ? "" : timeFormat(qMenus.EndTime[m], "HH:mm"),
                "SortOrder": qMenus.SortOrder[m]
            });
        }

        // Auto-select menu based on current time when no specific menu requested
        if (menuID == 0 && qMenus.recordCount > 1) {
            now = timeFormat(now(), "HH:mm");
            dayOfWeek = dayOfWeek(now()); // 1=Sun, 2=Mon, ... 7=Sat
            dayBit = 2 ^ (dayOfWeek - 1); // bitmask: 1=Sun, 2=Mon, 4=Tue, etc.
            activeMenuIds = [];

            for (m = 1; m <= qMenus.recordCount; m++) {
                // Check if menu is active today (days bitmask)
                menuDays = qMenus.DaysActive[m];
                if (bitAnd(menuDays, dayBit) == 0) continue;

                // Check time range
                hasStart = !isNull(qMenus.StartTime[m]);
                hasEnd = !isNull(qMenus.EndTime[m]);
                if (hasStart && hasEnd) {
                    startT = timeFormat(qMenus.StartTime[m], "HH:mm");
                    endT = timeFormat(qMenus.EndTime[m], "HH:mm");
                    if (now >= startT && now <= endT) {
                        arrayAppend(activeMenuIds, qMenus.ID[m]);
                    }
                } else {
                    // No time restriction = always active
                    arrayAppend(activeMenuIds, qMenus.ID[m]);
                }
            }

            // If exactly one menu is active now, auto-select it
            if (arrayLen(activeMenuIds) == 1) {
                menuID = activeMenuIds[1];
            } else if (arrayLen(activeMenuIds) > 1 && defaultMenuID > 0 && arrayFind(activeMenuIds, defaultMenuID)) {
                // Multiple menus active - use business default if it's among the active ones
                menuID = defaultMenuID;
            }
            // If multiple match with no default, or none match, show all (menuID stays 0)
        }
    } catch (any e) {
        // Menus table might not exist yet
    }

    // Check if Categories table has data for this business
    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;
    }

    if (hasCategoriesData) {
        // OLD SCHEMA: Use Categories table for categories
        // Build menu filter clause
        menuFilter = "";
        menuParams = { businessID: businessID };
        if (menuID > 0) {
            menuFilter = " AND MenuID = :menuID";
            menuParams["menuID"] = menuID;
        }

        qCategories = queryTimed("
            SELECT
                ID,
                Name,
                SortOrder as SortOrder,
                MenuID
            FROM Categories
            WHERE BusinessID = :businessID #menuFilter#
            ORDER BY SortOrder, Name
        ", menuParams, { datasource: "payfrit" });

        // Get menu items - items that belong to categories (not modifiers)
        qItems = queryTimed("
            SELECT
                i.ID,
                i.CategoryID as CategoryItemID,
                i.Name,
                i.Description,
                i.Price,
                i.SortOrder,
                i.IsActive
            FROM Items i
            WHERE i.BusinessID = :businessID
              AND i.IsActive = 1
              AND i.CategoryID > 0
            ORDER BY i.SortOrder, i.Name
        ", { businessID: businessID }, { datasource: "payfrit" });

        // Get direct modifiers (items with ParentItemID pointing to menu items, not categories)
        qDirectModifiers = queryTimed("
            SELECT
                m.ID as ItemID,
                m.ParentItemID as ParentItemID,
                m.Name,
                m.Price,
                m.IsCheckedByDefault as IsDefault,
                m.SortOrder,
                m.RequiresChildSelection as RequiresSelection,
                m.MaxNumSelectionReq as MaxSelections
            FROM Items m
            WHERE m.BusinessID = :businessID
              AND m.IsActive = 1
              AND m.ParentItemID > 0
              AND (m.CategoryID = 0 OR m.CategoryID IS NULL)
            ORDER BY m.SortOrder, m.Name
        ", { businessID: businessID }, { datasource: "payfrit" });

    } else {
        // NEW UNIFIED SCHEMA: Categories are Items at ParentID=0 with children
        qCategories = queryTimed("
            SELECT DISTINCT
                p.ID as CategoryID,
                p.Name as Name,
                p.SortOrder
            FROM Items p
            INNER JOIN Items c ON c.ParentItemID = p.ID
            WHERE p.BusinessID = :businessID
              AND p.ParentItemID = 0
              AND p.IsActive = 1
              AND NOT EXISTS (
                  SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = p.ID
              )
            ORDER BY p.SortOrder, p.Name
        ", { businessID: businessID }, { datasource: "payfrit" });

        qItems = queryTimed("
            SELECT
                i.ID,
                i.ParentItemID as CategoryItemID,
                i.Name,
                i.Description,
                i.Price,
                i.SortOrder,
                i.IsActive
            FROM Items i
            INNER JOIN Items cat ON cat.ID = i.ParentItemID
            WHERE i.BusinessID = :businessID
              AND i.IsActive = 1
              AND cat.ParentItemID = 0
              AND NOT EXISTS (
                  SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = cat.ID
              )
            ORDER BY i.SortOrder, i.Name
        ", { businessID: businessID }, { datasource: "payfrit" });

        qDirectModifiers = queryTimed("
            SELECT
                m.ID as ItemID,
                m.ParentItemID as ParentItemID,
                m.Name,
                m.Price,
                m.IsCheckedByDefault as IsDefault,
                m.SortOrder,
                m.RequiresChildSelection as RequiresSelection,
                m.MaxNumSelectionReq as MaxSelections
            FROM Items m
            WHERE m.BusinessID = :businessID
              AND m.IsActive = 1
              AND m.ParentItemID > 0
            ORDER BY m.SortOrder, m.Name
        ", { businessID: businessID }, { datasource: "payfrit" });
    }

    // Collect menu item IDs for filtering template links
    menuItemIds = [];
    for (i = 1; i <= qItems.recordCount; i++) {
        arrayAppend(menuItemIds, qItems.ID[i]);
    }

    // Get template links ONLY for this business's menu items
    qTemplateLinks = queryNew("ParentItemID,TemplateItemID,SortOrder");
    if (arrayLen(menuItemIds) > 0) {
        qTemplateLinks = queryTimed("
            SELECT
                tl.ItemID as ParentItemID,
                tl.TemplateItemID,
                tl.SortOrder
            FROM lt_ItemID_TemplateItemID tl
            WHERE tl.ItemID IN (:itemIds)
            ORDER BY tl.ItemID, tl.SortOrder
        ", { itemIds: { value: arrayToList(menuItemIds), cfsqltype: "cf_sql_varchar", list: true } }, { datasource: "payfrit" });
    }

    // Get templates for this business only
    qTemplates = queryTimed("
        SELECT DISTINCT
            t.ID as ItemID,
            t.Name,
            t.Price,
            t.IsCheckedByDefault as IsDefault,
            t.SortOrder,
            t.RequiresChildSelection as RequiresSelection,
            t.MaxNumSelectionReq as MaxSelections
        FROM Items t
        WHERE t.BusinessID = :businessID
          AND (t.CategoryID = 0 OR t.CategoryID IS NULL)
          AND t.ParentItemID = 0
          AND t.IsActive = 1
        ORDER BY t.SortOrder, t.Name
    ", { businessID: businessID }, { datasource: "payfrit" });

    // Get template children (options within templates)
    templateIds = [];
    for (i = 1; i <= qTemplates.recordCount; i++) {
        arrayAppend(templateIds, qTemplates.ItemID[i]);
    }

    qTemplateChildren = queryNew("ItemID,ParentItemID,Name,Price,IsDefault,SortOrder,RequiresSelection,MaxSelections");
    if (arrayLen(templateIds) > 0) {
        qTemplateChildren = queryTimed("
            SELECT
                c.ID as ItemID,
                c.ParentItemID as ParentItemID,
                c.Name,
                c.Price,
                c.IsCheckedByDefault as IsDefault,
                c.SortOrder,
                c.RequiresChildSelection as RequiresSelection,
                c.MaxNumSelectionReq as MaxSelections
            FROM Items c
            WHERE c.ParentItemID IN (:templateIds)
              AND c.IsActive = 1
            ORDER BY c.SortOrder, c.Name
        ", { templateIds: { value: arrayToList(templateIds), cfsqltype: "cf_sql_varchar", list: true } }, { datasource: "payfrit" });
    }

    // Build templates lookup with their options
    templatesById = {};
    for (i = 1; i <= qTemplates.recordCount; i++) {
        templateID = qTemplates.ItemID[i];
        options = buildOptionsTree(qTemplateChildren, templateID);
        templatesById[templateID] = {
            "id": "mod_" & qTemplates.ItemID[i],
            "dbId": qTemplates.ItemID[i],
            "name": qTemplates.Name[i],
            "price": qTemplates.Price[i],
            "isDefault": qTemplates.IsDefault[i] == 1 ? true : false,
            "sortOrder": qTemplates.SortOrder[i],
            "isTemplate": true,
            "requiresSelection": isNull(qTemplates.RequiresSelection[i]) ? false : (qTemplates.RequiresSelection[i] == 1),
            "maxSelections": isNull(qTemplates.MaxSelections[i]) ? 0 : qTemplates.MaxSelections[i],
            "options": options
        };
    }

    // Build template links lookup by parent ItemID
    templateLinksByItem = {};
    for (i = 1; i <= qTemplateLinks.recordCount; i++) {
        parentID = qTemplateLinks.ParentItemID[i];
        templateID = qTemplateLinks.TemplateItemID[i];

        if (!structKeyExists(templateLinksByItem, parentID)) {
            templateLinksByItem[parentID] = [];
        }

        if (structKeyExists(templatesById, templateID)) {
            tmpl = duplicate(templatesById[templateID]);
            tmpl["sortOrder"] = qTemplateLinks.SortOrder[i];
            arrayAppend(templateLinksByItem[parentID], tmpl);
        }
    }

    // Build nested direct modifiers for each menu item
    directModsByItem = {};
    for (itemId in menuItemIds) {
        options = buildOptionsTree(qDirectModifiers, itemId);
        if (arrayLen(options) > 0) {
            directModsByItem[itemId] = options;
        }
    }

    // Build items lookup by CategoryID
    itemsByCategory = {};
    for (i = 1; i <= qItems.recordCount; i++) {
        catID = qItems.CategoryItemID[i];
        if (!structKeyExists(itemsByCategory, catID)) {
            itemsByCategory[catID] = [];
        }

        itemID = qItems.ID[i];

        // Get template-linked modifiers
        itemModifiers = structKeyExists(templateLinksByItem, itemID) ? duplicate(templateLinksByItem[itemID]) : [];

        // Add direct modifiers
        if (structKeyExists(directModsByItem, itemID)) {
            directMods = directModsByItem[itemID];
            for (j = 1; j <= arrayLen(directMods); j++) {
                arrayAppend(itemModifiers, directMods[j]);
            }
        }

        // Sort modifiers by sortOrder
        if (arrayLen(itemModifiers) > 1) {
            arraySort(itemModifiers, function(a, b) {
                return a.sortOrder - b.sortOrder;
            });
        }

        // Check for existing item photo
        itemImageUrl = "";
        itemsDir = expandPath("/uploads/items");
        for (ext in ["jpg","jpeg","png","gif","webp","JPG","JPEG","PNG","GIF","WEBP"]) {
            if (fileExists(itemsDir & "/" & qItems.ID[i] & "." & ext)) {
                itemImageUrl = "/uploads/items/" & qItems.ID[i] & "." & ext;
                break;
            }
        }

        arrayAppend(itemsByCategory[catID], {
            "id": "item_" & qItems.ID[i],
            "dbId": qItems.ID[i],
            "name": qItems.Name[i],
            "description": isNull(qItems.Description[i]) ? "" : qItems.Description[i],
            "price": qItems.Price[i],
            "imageUrl": len(itemImageUrl) ? itemImageUrl : javaCast("null", ""),
            "photoTaskId": javaCast("null", ""),
            "modifiers": itemModifiers,
            "sortOrder": qItems.SortOrder[i]
        });
    }

    // Build categories array
    categories = [];
    catIndex = 0;
    for (i = 1; i <= qCategories.recordCount; i++) {
        catID = qCategories.ID[i];
        catItems = structKeyExists(itemsByCategory, catID) ? itemsByCategory[catID] : [];

        catStruct = {
            "id": "cat_" & qCategories.ID[i],
            "dbId": qCategories.ID[i],
            "name": qCategories.Name[i],
            "description": "",
            "sortOrder": catIndex,
            "items": catItems
        };

        // Include MenuID if available (legacy schema with Categories table)
        if (hasCategoriesData) {
            try {
                catStruct["menuId"] = isNull(qCategories.MenuID[i]) ? 0 : val(qCategories.MenuID[i]);
            } catch (any e) {
                catStruct["menuId"] = 0;
            }
        }

        arrayAppend(categories, catStruct);
        catIndex++;
    }

    // Build template library array
    templateLibrary = [];
    for (templateID in templatesById) {
        arrayAppend(templateLibrary, templatesById[templateID]);
    }

    // Get business brand color
    brandColor = "";
    try {
        qBrand = queryTimed("
            SELECT BrandColor AS BusinessBrandColor FROM Businesses WHERE ID = :bizId
        ", { bizId: businessID }, { datasource: "payfrit" });
        if (qBrand.recordCount > 0 && len(trim(qBrand.BusinessBrandColor))) {
            brandColor = left(qBrand.BusinessBrandColor, 1) == chr(35) ? qBrand.BusinessBrandColor : chr(35) & qBrand.BusinessBrandColor;
        }
    } catch (any e) {
        // Column may not exist yet, ignore
    }

    response["OK"] = true;
    response["MENU"] = { "categories": categories };
    response["MENUS"] = allMenus;
    response["SELECTED_MENU_ID"] = menuID;
    response["DEFAULT_MENU_ID"] = defaultMenuID;
    response["TEMPLATES"] = templateLibrary;
    response["BRANDCOLOR"] = brandColor;
    response["CATEGORY_COUNT"] = arrayLen(categories);
    response["TEMPLATE_COUNT"] = arrayLen(templateLibrary);
    response["MENU_COUNT"] = arrayLen(allMenus);
    response["SCHEMA"] = hasCategoriesData ? "legacy" : "unified";

    totalItems = 0;
    for (cat in categories) {
        totalItems += arrayLen(cat.items);
    }
    response["ITEM_COUNT"] = totalItems;

} catch (any e) {
    response["ERROR"] = "server_error";
    response["MESSAGE"] = e.message;
    response["DETAIL"] = e.detail ?: "";
}

try{logPerf(0);}catch(any e){}
writeOutput(serializeJSON(response));
</cfscript>
