<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">

<cffunction name="readJsonBody" access="public" returntype="struct" output="false">
  <cfset var raw = getHttpRequestData().content>
  <cfif isNull(raw) OR len(trim(raw)) EQ 0>
    <cfreturn {}>
  </cfif>
  <cftry>
    <cfset var data = deserializeJSON(raw)>
    <cfif isStruct(data)>
      <cfreturn data>
    <cfelse>
      <cfreturn {}>
    </cfif>
    <cfcatch>
      <cfreturn {}>
    </cfcatch>
  </cftry>
</cffunction>

<cffunction name="apiAbort" access="public" returntype="void" output="true">
  <cfargument name="payload" type="struct" required="true">
  <cfcontent type="application/json; charset=utf-8">
  <cfoutput>#serializeJSON(arguments.payload)#</cfoutput>
  <cfabort>
</cffunction>

<cfset data = readJsonBody()>
<cfset BusinessID = 0>
<cfif structKeyExists(data, "BusinessID")>
  <cfset BusinessID = val(data.BusinessID)>
</cfif>

<!--- Optional OrderTypeID for channel filtering (1=Dine-In, 2=Takeaway, 3=Delivery) --->
<cfset OrderTypeID = 0>
<cfif structKeyExists(data, "OrderTypeID")>
  <cfset OrderTypeID = val(data.OrderTypeID)>
</cfif>

<!--- Optional MenuID for menu filtering (0 = all active menus) --->
<cfset requestedMenuID = 0>
<cfif structKeyExists(data, "MenuID")>
  <cfset requestedMenuID = val(data.MenuID)>
</cfif>

<cfif BusinessID LTE 0>
  <cfset apiAbort({ "OK": false, "ERROR": "missing_businessid", "MESSAGE": "BusinessID is required.", "DETAIL": "" })>
</cfif>

<!--- Get current time and day for schedule filtering --->
<cfset currentTime = timeFormat(now(), "HH:mm:ss")>
<cfset currentDayID = dayOfWeek(now())>

<cfset menuList = []>
<cftry>
  <!--- Check if new schema is active (BusinessID column exists and has data) --->
  <cfset newSchemaActive = false>
  <cftry>
    <cfset qCheck = queryTimed(
      "SELECT COUNT(*) as cnt FROM Items WHERE BusinessID = ? AND BusinessID > 0",
      [ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
      { datasource = "payfrit" }
    )>
    <cfset newSchemaActive = (qCheck.cnt GT 0)>
    <cfcatch>
      <!--- Column doesn't exist yet, use old schema --->
      <cfset newSchemaActive = false>
    </cfcatch>
  </cftry>

  <cfif newSchemaActive>
    <!--- NEW SCHEMA: Items have BusinessID, try Categories table first, fallback to parent Items --->
    <!--- Check if Categories table has data for this business --->
    <cfset hasCategoriesData = false>
    <cftry>
      <cfset qCatCheck = queryTimed(
        "SELECT COUNT(*) as cnt FROM Categories WHERE BusinessID = ?",
        [ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
        { datasource = "payfrit" }
      )>
      <cfset hasCategoriesData = (qCatCheck.cnt GT 0)>
      <cfcatch>
        <cfset hasCategoriesData = false>
      </cfcatch>
    </cftry>

    <cfif hasCategoriesData>
      <!--- Use Categories table with CategoryID --->
      <!--- Get all active menus for this business (for chip selector) --->
      <cfset activeMenuIds = "">
      <cfset menuList = []>
      <cftry>
        <cfset qAllMenus = queryTimed(
          "
            SELECT ID, Name FROM Menus
            WHERE BusinessID = :bizId
              AND IsActive = 1
            ORDER BY SortOrder, Name
          ",
          {
            bizId: { value = BusinessID, cfsqltype = "cf_sql_integer" }
          },
          { datasource = "payfrit" }
        )>
        <cfif requestedMenuID GT 0>
          <!--- User selected a specific menu --->
          <cfset activeMenuIds = requestedMenuID>
        <cfelse>
          <!--- No specific menu selected — show all items from all menus --->
          <cfset activeMenuIds = valueList(qAllMenus.ID)>
        </cfif>
        <!--- Build menu list for the response (all active menus, always) --->
        <cfloop query="qAllMenus">
          <cfset arrayAppend(menuList, {
            "MenuID": qAllMenus.ID,
            "Name": qAllMenus.Name
          })>
        </cfloop>
        <cfcatch>
          <!--- Menus table might not exist yet --->
        </cfcatch>
      </cftry>

      <!--- Get category headers as virtual items --->
      <!--- Apply schedule filtering, order type filtering, and menu filtering --->
      <cfset qCategories = queryTimed(
        "
          SELECT
            ID,
            Name,
            SortOrder,
            OrderTypes,
            ScheduleStart,
            ScheduleEnd,
            ScheduleDays,
            MenuID
          FROM Categories
          WHERE BusinessID = :bizId
            AND (
              :orderTypeId = 0
              OR FIND_IN_SET(:orderTypeId, OrderTypes) > 0
            )
            AND (
              ScheduleStart IS NULL
              OR ScheduleEnd IS NULL
              OR (
                TIME(:currentTime) >= ScheduleStart
                AND TIME(:currentTime) <= ScheduleEnd
              )
            )
            AND (
              ScheduleDays IS NULL
              OR ScheduleDays = ''
              OR FIND_IN_SET(:currentDay, ScheduleDays) > 0
            )
            AND (
              MenuID IS NULL
              OR MenuID = 0
              #len(activeMenuIds) ? "OR MenuID IN (#activeMenuIds#)" : ""#
            )
          ORDER BY SortOrder
        ",
        {
          bizId: { value = BusinessID, cfsqltype = "cf_sql_integer" },
          orderTypeId: { value = OrderTypeID, cfsqltype = "cf_sql_integer" },
          currentTime: { value = currentTime, cfsqltype = "cf_sql_varchar" },
          currentDay: { value = currentDayID, cfsqltype = "cf_sql_integer" }
        },
        { datasource = "payfrit" }
      )>

      <!--- Get menu items --->
      <!--- Exclude old-style category headers (ParentID=0 AND CategoryID=0 AND no children with CategoryID>0) --->
      <!--- These are legacy category headers that should be replaced by Categories table entries --->
      <!--- Only include items from visible categories (after schedule/channel filtering) --->
      <cfset visibleCategoryIds = valueList(qCategories.ID)>
      <cfif len(trim(visibleCategoryIds)) EQ 0>
        <cfset visibleCategoryIds = "0">
      </cfif>
      <cfset q = queryTimed(
        "
          SELECT
            i.ID,
            i.CategoryID,
            c.Name AS CategoryName,
            i.Name AS ItemName,
            i.Description,
            i.ParentItemID,
            i.Price,
            i.IsActive,
            i.IsCheckedByDefault,
            i.RequiresChildSelection,
            i.MaxNumSelectionReq,
            i.IsCollapsible,
            i.SortOrder,
            i.StationID,
            s.Name AS StationName,
            s.Color,
            c.MenuID
          FROM Items i
          LEFT JOIN Categories c ON c.ID = i.CategoryID
          LEFT JOIN Stations s ON s.ID = i.StationID
          WHERE i.BusinessID = :bizId
            AND i.IsActive = 1
            AND i.CategoryID IN (#visibleCategoryIds#)
            AND NOT EXISTS (SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = i.ID)
            AND NOT EXISTS (SELECT 1 FROM lt_ItemID_TemplateItemID tl2 WHERE tl2.TemplateItemID = i.ParentItemID)
            AND NOT (i.ParentItemID = 0 AND i.CategoryID = 0 AND i.Price = 0)
          ORDER BY COALESCE(c.SortOrder, 999), i.SortOrder, i.ID
        ",
        { bizId: { value = BusinessID, cfsqltype = "cf_sql_integer" } },
        { datasource = "payfrit" }
      )>
    <cfelse>
      <!--- Fallback: Derive categories from parent Items --->
      <cfset q = queryTimed(
        "
          SELECT
            i.ID,
            CASE
              WHEN i.ParentItemID = 0 AND i.IsCollapsible = 0 THEN i.ID
              ELSE COALESCE(
                (SELECT cat.ID FROM Items cat
                 WHERE cat.ID = i.ParentItemID
                   AND cat.ParentItemID = 0
                   AND cat.IsCollapsible = 0),
                0
              )
            END as CategoryID,
            CASE
              WHEN i.ParentItemID = 0 AND i.IsCollapsible = 0 THEN i.Name
              ELSE COALESCE(
                (SELECT cat.Name FROM Items cat
                 WHERE cat.ID = i.ParentItemID
                   AND cat.ParentItemID = 0
                   AND cat.IsCollapsible = 0),
                ''
              )
            END as Name,
            i.Name,
            i.Description,
            i.ParentItemID,
            i.Price,
            i.IsActive,
            i.IsCheckedByDefault,
            i.RequiresChildSelection,
            i.MaxNumSelectionReq,
            i.IsCollapsible,
            i.SortOrder,
            i.StationID,
            s.Name,
            s.Color
          FROM Items i
          LEFT JOIN Stations s ON s.ID = i.StationID
          WHERE i.BusinessID = ?
            AND i.IsActive = 1
            AND NOT EXISTS (SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = i.ID)
            AND NOT EXISTS (SELECT 1 FROM lt_ItemID_TemplateItemID tl2 WHERE tl2.TemplateItemID = i.ParentItemID)
            AND (
              i.ParentItemID > 0
              OR (i.ParentItemID = 0 AND i.IsCollapsible = 0)
            )
          ORDER BY i.ParentItemID, i.SortOrder, i.ID
        ",
        [ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
        { datasource = "payfrit" }
      )>
    </cfif>
  <cfelse>
    <!--- OLD SCHEMA: Use Categories table --->
    <cfset q = queryTimed(
      "
        SELECT
          i.ID,
          i.CategoryID,
          c.Name,
          i.Name,
          i.Description,
          i.ParentItemID,
          i.Price,
          i.IsActive,
          i.IsCheckedByDefault,
          i.RequiresChildSelection,
          i.MaxNumSelectionReq,
          i.IsCollapsible,
          i.SortOrder,
          i.StationID,
          s.Name,
          s.Color
        FROM Items i
        INNER JOIN Categories c ON c.ID = i.CategoryID
        LEFT JOIN Stations s ON s.ID = i.StationID
        WHERE c.BusinessID = ?
        ORDER BY i.ParentItemID, i.SortOrder, i.ID
      ",
      [ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
      { datasource = "payfrit" }
    )>
  </cfif>

  <cfset rows = []>

  <!--- Build set of category IDs that actually have items --->
  <cfset categoriesWithItems = {}>
  <cfloop query="q">
    <cfif q.CategoryID GT 0>
      <cfset categoriesWithItems[q.CategoryID] = true>
    </cfif>
  </cfloop>

  <!--- For unified schema with Categories table, add category headers (only if they have items) --->
  <cfif newSchemaActive AND isDefined("qCategories")>
    <cfloop query="qCategories">
      <cfif structKeyExists(categoriesWithItems, qCategories.ID)>
        <!--- Add category as a virtual parent item --->
        <cfset arrayAppend(rows, {
          "ItemID": qCategories.ID,
          "CategoryID": qCategories.ID,
          "Name": qCategories.Name,
          "Description": "",
          "ParentItemID": 0,
          "Price": 0,
          "IsActive": 1,
          "IsCheckedByDefault": 0,
          "RequiresChildSelection": 0,
          "MaxNumSelectionReq": 0,
          "IsCollapsible": 0,
          "SortOrder": qCategories.SortOrder,
          "MenuID": isNull(qCategories.MenuID) ? 0 : val(qCategories.MenuID),
          "StationID": "",
          "ItemName": "",
          "ItemColor": ""
        })>
      </cfif>
    </cfloop>
  </cfif>

  <!--- Build a set of category IDs for quick lookup --->
  <cfset categoryIdSet = {}>
  <cfif isDefined("qCategories")>
    <cfloop query="qCategories">
      <cfset categoryIdSet[qCategories.ID] = true>
    </cfloop>
  </cfif>

  <cfloop query="q">
    <!--- For unified schema with Categories: set ParentItemID to CategoryID for top-level items --->
    <!--- Remap old parent IDs to category IDs --->
    <cfset effectiveParentID = q.ParentItemID>
    <cfif newSchemaActive AND isDefined("qCategories") AND q.CategoryID GT 0>
      <cfif q.ParentItemID EQ 0>
        <!--- Item has no parent but has a category - link to category --->
        <cfset effectiveParentID = q.CategoryID>
      <cfelseif structKeyExists(categoryIdSet, q.ParentItemID)>
        <!--- Item's parent IS a category ID - this is correct, keep it --->
        <cfset effectiveParentID = q.ParentItemID>
      <cfelseif NOT structKeyExists(categoryIdSet, q.ParentItemID)>
        <!--- Parent ID is an old-style category header - remap to CategoryID --->
        <cfset effectiveParentID = q.CategoryID>
      </cfif>
    </cfif>

    <cfset itemMenuID = 0>
    <cftry>
      <cfset itemMenuID = isNull(q.MenuID) ? 0 : val(q.MenuID)>
      <cfcatch><cfset itemMenuID = 0></cfcatch>
    </cftry>
    <!--- Use aliased columns if available, fall back to generic Name --->
    <cfset itemName = "">
    <cfset catName = "">
    <cftry>
      <cfset itemName = q.ItemName>
      <cfset catName = q.CategoryName>
      <cfcatch>
        <!--- Fallback for old schema queries without aliases --->
        <cfset itemName = q.Name>
        <cfset catName = q.Name>
      </cfcatch>
    </cftry>
    <cfset arrayAppend(rows, {
      "ItemID": q.ID,
      "CategoryID": q.CategoryID,
      "Name": len(trim(itemName)) ? itemName : catName,
      "Description": q.Description,
      "ParentItemID": effectiveParentID,
      "Price": q.Price,
      "IsActive": q.IsActive,
      "IsCheckedByDefault": q.IsCheckedByDefault,
      "RequiresChildSelection": q.RequiresChildSelection,
      "MaxNumSelectionReq": q.MaxNumSelectionReq,
      "IsCollapsible": q.IsCollapsible,
      "SortOrder": q.SortOrder,
      "MenuID": itemMenuID,
      "StationID": len(trim(q.StationID)) ? q.StationID : "",
      "ItemName": len(trim(catName)) ? catName : "",
      "ItemColor": len(trim(q.Color)) ? q.Color : ""
    })>
  </cfloop>

  <!--- For unified schema: Add template-linked modifiers as virtual children of menu items --->
  <cfif newSchemaActive>
    <!--- Get template links: which menu items use which templates --->
    <cfset qTemplateLinks = queryTimed(
      "
        SELECT
          tl.ItemID as MenuItemID,
          tmpl.ID as TemplateItemID,
          tmpl.Name as TemplateName,
          tmpl.Description as TemplateDescription,
          tmpl.RequiresChildSelection as TemplateRequired,
          tmpl.MaxNumSelectionReq as TemplateMaxSelections,
          tmpl.IsCollapsible as TemplateIsCollapsible,
          tl.SortOrder as TemplateSortOrder
        FROM lt_ItemID_TemplateItemID tl
        INNER JOIN Items tmpl ON tmpl.ID = tl.TemplateItemID AND tmpl.IsActive = 1
        INNER JOIN Items menuItem ON menuItem.ID = tl.ItemID
        WHERE menuItem.BusinessID = ?
          AND menuItem.IsActive = 1
        ORDER BY tl.ItemID, tl.SortOrder
      ",
      [ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
      { datasource = "payfrit" }
    )>

    <!--- Get template options --->
    <cfset qTemplateOptions = queryTimed(
      "
        SELECT DISTINCT
          opt.ID as OptionItemID,
          opt.ParentItemID as TemplateItemID,
          opt.Name as OptionName,
          opt.Description as OptionDescription,
          opt.Price as OptionPrice,
          opt.IsCheckedByDefault as OptionIsDefault,
          opt.SortOrder as OptionSortOrder
        FROM Items opt
        INNER JOIN lt_ItemID_TemplateItemID tl ON tl.TemplateItemID = opt.ParentItemID
        INNER JOIN Items menuItem ON menuItem.ID = tl.ItemID
        WHERE menuItem.BusinessID = ?
          AND menuItem.IsActive = 1
          AND opt.IsActive = 1
        ORDER BY opt.ParentItemID, opt.SortOrder
      ",
      [ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
      { datasource = "payfrit" }
    )>

    <!--- Build template options map: templateID -> [options] --->
    <cfset templateOptionsMap = {}>
    <cfloop query="qTemplateOptions">
      <cfif NOT structKeyExists(templateOptionsMap, qTemplateOptions.TemplateItemID)>
        <cfset templateOptionsMap[qTemplateOptions.TemplateItemID] = []>
      </cfif>
      <cfset arrayAppend(templateOptionsMap[qTemplateOptions.TemplateItemID], {
        "ItemID": qTemplateOptions.OptionItemID,
        "Name": qTemplateOptions.OptionName,
        "Description": qTemplateOptions.OptionDescription,
        "Price": qTemplateOptions.OptionPrice,
        "IsCheckedByDefault": qTemplateOptions.OptionIsDefault,
        "SortOrder": qTemplateOptions.OptionSortOrder
      })>
    </cfloop>

    <!--- Add templates and their options as virtual children --->
    <!--- Use virtual IDs to make each template instance unique per menu item --->
    <!--- Virtual ID format: menuItemID * 100000 + templateID for templates --->
    <!---                    menuItemID * 100000 + optionID for options --->
    <cfset addedTemplates = {}>
    <cfloop query="qTemplateLinks">
      <cfset menuItemID = qTemplateLinks.MenuItemID>
      <cfset templateID = qTemplateLinks.TemplateItemID>
      <cfset linkKey = menuItemID & "_" & templateID>

      <!--- Skip duplicates --->
      <cfif structKeyExists(addedTemplates, linkKey)>
        <cfcontinue>
      </cfif>
      <cfset addedTemplates[linkKey] = true>

      <!--- Generate unique virtual ID for this template instance --->
      <cfset virtualTemplateID = menuItemID * 100000 + templateID>

      <!--- Add template as modifier group (child of menu item) --->
      <cfset arrayAppend(rows, {
        "ItemID": virtualTemplateID,
        "CategoryID": 0,
        "Name": "",
        "Name": qTemplateLinks.TemplateName,
        "Description": qTemplateLinks.TemplateDescription,
        "ParentItemID": menuItemID,
        "Price": 0,
        "IsActive": 1,
        "IsCheckedByDefault": 0,
        "RequiresChildSelection": qTemplateLinks.TemplateRequired,
        "MaxNumSelectionReq": qTemplateLinks.TemplateMaxSelections,
        "IsCollapsible": qTemplateLinks.TemplateIsCollapsible,
        "SortOrder": qTemplateLinks.TemplateSortOrder,
        "StationID": "",
        "ItemName": "",
        "ItemColor": ""
      })>

      <!--- Add template options as children of virtual template --->
      <cfif structKeyExists(templateOptionsMap, templateID)>
        <cfloop array="#templateOptionsMap[templateID]#" index="opt">
          <!--- Generate unique virtual ID for this option instance --->
          <cfset virtualOptionID = menuItemID * 100000 + opt.ItemID>
          <cfset arrayAppend(rows, {
            "ItemID": virtualOptionID,
            "CategoryID": 0,
            "Name": "",
            "Name": opt.Name,
            "Description": opt.Description,
            "ParentItemID": virtualTemplateID,
            "Price": opt.Price,
            "IsActive": 1,
            "IsCheckedByDefault": opt.IsCheckedByDefault,
            "RequiresChildSelection": 0,
            "MaxNumSelectionReq": 0,
            "IsCollapsible": 0,
            "SortOrder": opt.SortOrder,
            "StationID": "",
            "ItemName": "",
            "ItemColor": ""
          })>
        </cfloop>
      </cfif>
    </cfloop>
  </cfif>

  <!--- Get brand color, tax rate, and header image for this business --->
  <cfset brandColor = "">
  <cfset businessTaxRate = 0>
  <cfset headerImageUrl = "">
  <cftry>
    <cfset qBrand = queryTimed(
      "SELECT BrandColor AS BusinessBrandColor, TaxRate, HeaderImageExtension FROM Businesses WHERE ID = ?",
      [ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
      { datasource = "payfrit" }
    )>
    <cfif qBrand.recordCount GT 0>
      <cfif len(trim(qBrand.BusinessBrandColor))>
        <cfset brandColor = left(qBrand.BusinessBrandColor, 1) EQ chr(35) ? qBrand.BusinessBrandColor : chr(35) & qBrand.BusinessBrandColor>
      </cfif>
      <cfif isNumeric(qBrand.TaxRate)>
        <cfset businessTaxRate = qBrand.TaxRate>
      </cfif>
      <cfif len(trim(qBrand.HeaderImageExtension))>
        <cfset headerImageUrl = "/uploads/headers/#BusinessID#.#qBrand.HeaderImageExtension#">
      </cfif>
    </cfif>
    <cfcatch>
      <!--- Column may not exist yet, ignore --->
    </cfcatch>
  </cftry>

  <cfset apiAbort({
    "OK": true,
    "ERROR": "",
    "Items": rows,
    "COUNT": arrayLen(rows),
    "SCHEMA": newSchemaActive ? "unified" : "legacy",
    "BRANDCOLOR": brandColor,
    "HEADERIMAGEURL": headerImageUrl,
    "TAXRATE": val(businessTaxRate),
    "Menus": menuList,
    "SelectedMenuID": requestedMenuID
  })>

  <cfcatch>
    <cfset apiAbort({
      "OK": false,
      "ERROR": "server_error",
      "MESSAGE": "DB error loading items",
      "DETAIL": cfcatch.message
    })>
  </cfcatch>
</cftry>
