<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>

<cffunction name="nextId" access="public" returntype="numeric" output="false">
  <cfargument name="tableName" type="string" required="true">
  <cfargument name="idField" type="string" required="true">

  <cfset var q = queryExecute(
    "SELECT IFNULL(MAX(#arguments.idField#),0) + 1 AS NextID FROM #arguments.tableName#",
    [],
    { datasource = "payfrit" }
  )>
  <cfreturn q.NextID>
</cffunction>

<cffunction name="attachDefaultChildren" access="public" returntype="void" output="false">
  <cfargument name="OrderID" type="numeric" required="true">
  <cfargument name="ParentLineItemID" type="numeric" required="true">
  <cfargument name="ParentItemID" type="numeric" required="true">

  <!--- Find immediate children where checked by default --->
  <cfset var qKids = queryExecute(
    "
      SELECT ItemID, ItemPrice
      FROM Items
      WHERE ItemParentItemID = ?
        AND ItemIsCheckedByDefault = 1
        AND ItemIsActive = b'1'
      ORDER BY ItemSortOrder, ItemID
    ",
    [ { value = arguments.ParentItemID, cfsqltype = "cf_sql_integer" } ],
    { datasource = "payfrit" }
  )>

  <cfloop query="qKids">
    <!--- If existing, undelete; else insert new --->
    <cfset var qExisting = queryExecute(
      "
        SELECT OrderLineItemID
        FROM OrderLineItems
        WHERE OrderLineItemOrderID = ?
          AND OrderLineItemParentOrderLineItemID = ?
          AND OrderLineItemItemID = ?
        LIMIT 1
      ",
      [
        { value = arguments.OrderID, cfsqltype = "cf_sql_integer" },
        { value = arguments.ParentLineItemID, cfsqltype = "cf_sql_integer" },
        { value = qKids.ItemID, cfsqltype = "cf_sql_integer" }
      ],
      { datasource = "payfrit" }
    )>

    <cfif qExisting.recordCount GT 0>
      <cfset queryExecute(
        "
          UPDATE OrderLineItems
          SET OrderLineItemIsDeleted = b'0'
          WHERE OrderLineItemID = ?
        ",
        [ { value = qExisting.OrderLineItemID, cfsqltype = "cf_sql_integer" } ],
        { datasource = "payfrit" }
      )>
      <cfset attachDefaultChildren(arguments.OrderID, qExisting.OrderLineItemID, qKids.ItemID)>
    <cfelse>
      <cfset var NewLIID = nextId("OrderLineItems","OrderLineItemID")>
      <cfset queryExecute(
        "
          INSERT INTO OrderLineItems (
            OrderLineItemID,
            OrderLineItemParentOrderLineItemID,
            OrderLineItemOrderID,
            OrderLineItemItemID,
            OrderLineItemStatusID,
            OrderLineItemPrice,
            OrderLineItemQuantity,
            OrderLineItemRemark,
            OrderLineItemIsDeleted,
            OrderLineItemAddedOn
          ) VALUES (
            ?,
            ?,
            ?,
            ?,
            0,
            ?,
            1,
            NULL,
            b'0',
            ?
          )
        ",
        [
          { value = NewLIID, cfsqltype = "cf_sql_integer" },
          { value = arguments.ParentLineItemID, cfsqltype = "cf_sql_integer" },
          { value = arguments.OrderID, cfsqltype = "cf_sql_integer" },
          { value = qKids.ItemID, cfsqltype = "cf_sql_integer" },
          { value = qKids.ItemPrice, cfsqltype = "cf_sql_decimal" },
          { value = now(), cfsqltype = "cf_sql_timestamp" }
        ],
        { datasource = "payfrit" }
      )>
      <cfset attachDefaultChildren(arguments.OrderID, NewLIID, qKids.ItemID)>
    </cfif>
  </cfloop>
</cffunction>

<cffunction name="loadCartPayload" access="public" returntype="struct" output="false">
  <cfargument name="OrderID" type="numeric" required="true">

  <cfset var out = {}>
  <cfset var qOrder = queryExecute(
    "
      SELECT
        OrderID,
        OrderUUID,
        OrderUserID,
        OrderBusinessID,
        OrderBusinessDeliveryMultiplier,
        OrderTypeID,
        OrderDeliveryFee,
        OrderStatusID,
        OrderAddressID,
        OrderPaymentID,
        OrderRemarks,
        OrderAddedOn,
        OrderLastEditedOn,
        OrderSubmittedOn,
        OrderServicePointID
      FROM Orders
      WHERE OrderID = ?
      LIMIT 1
    ",
    [ { value = arguments.OrderID, cfsqltype = "cf_sql_integer" } ],
    { datasource = "payfrit" }
  )>

  <cfif qOrder.recordCount EQ 0>
    <cfreturn { "OK": false, "ERROR": "not_found", "MESSAGE": "Order not found", "DETAIL": "" }>
  </cfif>

  <cfset out.ORDER = {
    "OrderID": qOrder.OrderID,
    "OrderUUID": qOrder.OrderUUID,
    "OrderUserID": qOrder.OrderUserID,
    "OrderBusinessID": qOrder.OrderBusinessID,
    "OrderBusinessDeliveryMultiplier": qOrder.OrderBusinessDeliveryMultiplier,
    "OrderTypeID": qOrder.OrderTypeID,
    "OrderDeliveryFee": qOrder.OrderDeliveryFee,
    "OrderStatusID": qOrder.OrderStatusID,
    "OrderAddressID": qOrder.OrderAddressID,
    "OrderPaymentID": qOrder.OrderPaymentID,
    "OrderRemarks": qOrder.OrderRemarks,
    "OrderAddedOn": qOrder.OrderAddedOn,
    "OrderLastEditedOn": qOrder.OrderLastEditedOn,
    "OrderSubmittedOn": qOrder.OrderSubmittedOn,
    "OrderServicePointID": qOrder.OrderServicePointID
  }>

  <cfset var qLI = queryExecute(
    "
      SELECT
        OrderLineItemID,
        OrderLineItemParentOrderLineItemID,
        OrderLineItemOrderID,
        OrderLineItemItemID,
        OrderLineItemStatusID,
        OrderLineItemPrice,
        OrderLineItemQuantity,
        OrderLineItemRemark,
        OrderLineItemIsDeleted,
        OrderLineItemAddedOn
      FROM OrderLineItems
      WHERE OrderLineItemOrderID = ?
      ORDER BY OrderLineItemID
    ",
    [ { value = arguments.OrderID, cfsqltype = "cf_sql_integer" } ],
    { datasource = "payfrit" }
  )>

  <cfset var rows = []>
  <cfloop query="qLI">
    <cfset arrayAppend(rows, {
      "OrderLineItemID": qLI.OrderLineItemID,
      "OrderLineItemParentOrderLineItemID": qLI.OrderLineItemParentOrderLineItemID,
      "OrderLineItemOrderID": qLI.OrderLineItemOrderID,
      "OrderLineItemItemID": qLI.OrderLineItemItemID,
      "OrderLineItemStatusID": qLI.OrderLineItemStatusID,
      "OrderLineItemPrice": qLI.OrderLineItemPrice,
      "OrderLineItemQuantity": qLI.OrderLineItemQuantity,
      "OrderLineItemRemark": qLI.OrderLineItemRemark,
      "OrderLineItemIsDeleted": qLI.OrderLineItemIsDeleted,
      "OrderLineItemAddedOn": qLI.OrderLineItemAddedOn
    })>
  </cfloop>

  <cfset out.ORDERLINEITEMS = rows>
  <cfset out.OK = true>
  <cfset out.ERROR = "">
  <cfreturn out>
</cffunction>

<cfset data = readJsonBody()>

<cfset OrderID = val( structKeyExists(data,"OrderID") ? data.OrderID : 0 )>
<cfset ParentLineItemID = val( structKeyExists(data,"ParentOrderLineItemID") ? data.ParentOrderLineItemID : 0 )>
<cfset ItemID = val( structKeyExists(data,"ItemID") ? data.ItemID : 0 )>
<cfset IsSelected = false>
<cfif structKeyExists(data, "IsSelected")>
  <cfset IsSelected = (data.IsSelected EQ true OR data.IsSelected EQ 1 OR (isSimpleValue(data.IsSelected) AND lcase(toString(data.IsSelected)) EQ "true"))>
</cfif>
<cfset Quantity = structKeyExists(data,"Quantity") ? val(data.Quantity) : 0>
<cfset Remark = structKeyExists(data,"Remark") ? toString(data.Remark) : "">

<cfif OrderID LTE 0 OR ItemID LTE 0>
  <cfset apiAbort({ "OK": false, "ERROR": "missing_params", "MESSAGE": "OrderID and ItemID are required.", "DETAIL": "" })>
</cfif>

<cftry>
  <!--- Load item price --->
  <cfset qItem = queryExecute(
    "
      SELECT ItemID, ItemPrice, ItemParentItemID, ItemIsActive
      FROM Items
      WHERE ItemID = ?
      LIMIT 1
    ",
    [ { value = ItemID, cfsqltype = "cf_sql_integer" } ],
    { datasource = "payfrit" }
  )>

  <cfif qItem.recordCount EQ 0 OR qItem.ItemIsActive NEQ true>
    <cfset apiAbort({ "OK": false, "ERROR": "bad_item", "MESSAGE": "Item not found or inactive.", "DETAIL": "" })>
  </cfif>

  <!--- Root vs modifier rules --->
  <cfif ParentLineItemID EQ 0>
    <!--- Root item quantity required when selecting --->
    <cfif IsSelected AND Quantity LTE 0>
      <cfset apiAbort({ "OK": false, "ERROR": "bad_quantity", "MESSAGE": "Root line items require Quantity > 0.", "DETAIL": "" })>
    </cfif>
  <cfelse>
    <!--- Modifier quantity is implicitly tied => force 1 when selecting --->
    <cfif IsSelected>
      <cfset Quantity = 1>
    <cfelse>
      <cfset Quantity = 1>
    </cfif>
  </cfif>

  <!--- Find existing line item (by order, parent LI, item) --->
  <cfset qExisting = queryExecute(
    "
      SELECT OrderLineItemID
      FROM OrderLineItems
      WHERE OrderLineItemOrderID = ?
        AND OrderLineItemParentOrderLineItemID = ?
        AND OrderLineItemItemID = ?
      LIMIT 1
    ",
    [
      { value = OrderID, cfsqltype = "cf_sql_integer" },
      { value = ParentLineItemID, cfsqltype = "cf_sql_integer" },
      { value = ItemID, cfsqltype = "cf_sql_integer" }
    ],
    { datasource = "payfrit" }
  )>

  <cfif qExisting.recordCount GT 0>
    <!--- Update existing --->
    <cfif IsSelected>
      <cfset queryExecute(
        "
          UPDATE OrderLineItems
          SET
            OrderLineItemIsDeleted = b'0',
            OrderLineItemQuantity = ?,
            OrderLineItemPrice = ?,
            OrderLineItemRemark = ?,
            OrderLineItemStatusID = 0
          WHERE OrderLineItemID = ?
        ",
        [
          { value = Quantity, cfsqltype = "cf_sql_integer" },
          { value = qItem.ItemPrice, cfsqltype = "cf_sql_decimal" },
          { value = Remark, cfsqltype = "cf_sql_varchar", null = (len(trim(Remark)) EQ 0) },
          { value = qExisting.OrderLineItemID, cfsqltype = "cf_sql_integer" }
        ],
        { datasource = "payfrit" }
      )>

      <!--- Attach default children for this node (recursively) --->
      <cfset attachDefaultChildren(OrderID, qExisting.OrderLineItemID, ItemID)>
    <cfelse>
      <cfset queryExecute(
        "
          UPDATE OrderLineItems
          SET OrderLineItemIsDeleted = b'1'
          WHERE OrderLineItemID = ?
        ",
        [ { value = qExisting.OrderLineItemID, cfsqltype = "cf_sql_integer" } ],
        { datasource = "payfrit" }
      )>
    </cfif>
  <cfelse>
    <!--- Insert new if selecting, otherwise no-op --->
    <cfif IsSelected>
      <cfset NewLIID = nextId("OrderLineItems","OrderLineItemID")>

      <cfset queryExecute(
        "
          INSERT INTO OrderLineItems (
            OrderLineItemID,
            OrderLineItemParentOrderLineItemID,
            OrderLineItemOrderID,
            OrderLineItemItemID,
            OrderLineItemStatusID,
            OrderLineItemPrice,
            OrderLineItemQuantity,
            OrderLineItemRemark,
            OrderLineItemIsDeleted,
            OrderLineItemAddedOn
          ) VALUES (
            ?,
            ?,
            ?,
            ?,
            0,
            ?,
            ?,
            ?,
            b'0',
            ?
          )
        ",
        [
          { value = NewLIID, cfsqltype = "cf_sql_integer" },
          { value = ParentLineItemID, cfsqltype = "cf_sql_integer" },
          { value = OrderID, cfsqltype = "cf_sql_integer" },
          { value = ItemID, cfsqltype = "cf_sql_integer" },
          { value = qItem.ItemPrice, cfsqltype = "cf_sql_decimal" },
          { value = (ParentLineItemID EQ 0 ? Quantity : 1), cfsqltype = "cf_sql_integer" },
          { value = Remark, cfsqltype = "cf_sql_varchar", null = (len(trim(Remark)) EQ 0) },
          { value = now(), cfsqltype = "cf_sql_timestamp" }
        ],
        { datasource = "payfrit" }
      )>

      <cfset attachDefaultChildren(OrderID, NewLIID, ItemID)>
    </cfif>
  </cfif>

  <!--- Touch order last edited --->
  <cfset queryExecute(
    "UPDATE Orders SET OrderLastEditedOn = ? WHERE OrderID = ?",
    [
      { value = now(), cfsqltype = "cf_sql_timestamp" },
      { value = OrderID, cfsqltype = "cf_sql_integer" }
    ],
    { datasource = "payfrit" }
  )>

  <cfset payload = loadCartPayload(OrderID)>
  <cfset apiAbort(payload)>

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