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

<cfscript>
/**
 * Get Order Detail
 * Returns full order info including line items, customer details, and staff who worked on the order
 *
 * GET: ?OrderID=123
 * POST: { OrderID: 123 }
 */

response = { "OK": false };

try {
    // Get OrderID from request
    orderID = 0;

    // Check URL params
    if (structKeyExists(url, "OrderID")) {
        orderID = val(url.OrderID);
    }

    // Check POST body
    if (orderID == 0) {
        requestBody = toString(getHttpRequestData().content);
        if (len(requestBody)) {
            requestData = deserializeJSON(requestBody);
            if (structKeyExists(requestData, "OrderID")) {
                orderID = val(requestData.OrderID);
            }
        }
    }

    if (orderID == 0) {
        response["ERROR"] = "missing_order_id";
        response["MESSAGE"] = "OrderID is required";
        writeOutput(serializeJSON(response));
        abort;
    }

    // Get order details
    qOrder = queryTimed("
        SELECT
            o.ID,
            o.BusinessID,
            o.UserID,
            o.ServicePointID,
            o.StatusID,
            o.OrderTypeID,
            o.Remarks,
            o.AddedOn,
            o.LastEditedOn,
            o.SubmittedOn,
            o.TipAmount,
            u.FirstName,
            u.LastName,
            u.ContactNumber,
            u.EmailAddress,
            sp.Name AS Name,
            sp.TypeID AS TypeID,
            b.Name AS BizName,
            b.TaxRate
        FROM Orders o
        LEFT JOIN Users u ON u.ID = o.UserID
        LEFT JOIN ServicePoints sp ON sp.ID = o.ServicePointID
        LEFT JOIN Businesses b ON b.ID = o.BusinessID
        WHERE o.ID = :orderID
    ", { orderID: orderID });

    if (qOrder.recordCount == 0) {
        response["ERROR"] = "order_not_found";
        response["MESSAGE"] = "Order not found";
        writeOutput(serializeJSON(response));
        abort;
    }

    // Get line items (excluding deleted items)
    qItems = queryTimed("
        SELECT
            oli.ID,
            oli.ItemID,
            oli.ParentOrderLineItemID,
            oli.Quantity,
            oli.Price,
            oli.Remark,
            i.Name,
            i.Price,
            i.IsCheckedByDefault
        FROM OrderLineItems oli
        INNER JOIN Items i ON i.ID = oli.ItemID
        WHERE oli.OrderID = :orderID
          AND oli.IsDeleted = 0
        ORDER BY oli.ID
    ", { orderID: orderID });

    // Build line items array with parent-child structure
    lineItems = [];
    itemsById = {};

    // First pass: create all items (use bracket notation to preserve key casing)
    for (row in qItems) {
        item = structNew("ordered");
        item["LineItemID"] = val(row.ID);
        item["ItemID"] = val(row.ID);
        item["ParentLineItemID"] = val(row.ParentOrderLineItemID);
        item["Name"] = row.Name ?: "";
        item["Quantity"] = val(row.Quantity);
        item["UnitPrice"] = val(row.Price);
        item["Remarks"] = row.Remark ?: "";
        item["IsDefault"] = (val(row.IsCheckedByDefault) == 1);
        item["Modifiers"] = [];
        itemsById[row.ID] = item;
    }

    // Second pass: build hierarchy
    for (row in qItems) {
        item = itemsById[row.ID];
        parentID = row.ParentOrderLineItemID;

        if (parentID > 0 && structKeyExists(itemsById, parentID)) {
            // This is a modifier - add to parent (use bracket notation)
            arrayAppend(itemsById[parentID]["Modifiers"], item);
        } else {
            // This is a top-level item
            arrayAppend(lineItems, item);
        }
    }

    // Calculate subtotal from root line items (use bracket notation)
    subtotal = 0;
    for (item in lineItems) {
        itemTotal = item["UnitPrice"] * item["Quantity"];
        // Add modifier prices
        for (mod in item["Modifiers"]) {
            itemTotal += mod["UnitPrice"] * mod["Quantity"];
        }
        subtotal += itemTotal;
    }

    // Calculate tax using business tax rate or default 8.25%
    taxRate = isNumeric(qOrder.TaxRate) && qOrder.TaxRate > 0 ? qOrder.TaxRate : 0.0825;
    tax = subtotal * taxRate;

    // Get tip from order
    tip = isNumeric(qOrder.TipAmount) ? qOrder.TipAmount : 0;

    // Calculate total
    total = subtotal + tax + tip;

    // Get staff who worked on this order (from Tasks table) with pending rating tokens
    qStaff = queryTimed("
        SELECT DISTINCT u.ID, u.FirstName,
               (SELECT r.AccessToken
                FROM TaskRatings r
                INNER JOIN Tasks t2 ON t2.ID = r.TaskID
                WHERE t2.OrderID = :orderID
                  AND r.ForUserID = u.ID
                  AND r.Direction = 'customer_rates_worker'
                  AND r.CompletedOn IS NULL
                  AND r.ExpiresOn > NOW()
                LIMIT 1) AS RatingToken
        FROM Tasks t
        INNER JOIN Users u ON u.ID = t.ClaimedByUserID
        WHERE t.OrderID = :orderID
          AND t.ClaimedByUserID > 0
    ", { orderID: orderID });

    // Build staff array with avatar URLs and rating tokens (use ordered structs)
    staff = [];
    for (row in qStaff) {
        staffMember = structNew("ordered");
        staffMember["UserID"] = row.ID;
        staffMember["FirstName"] = row.FirstName;
        staffMember["AvatarUrl"] = application.baseUrl & "/uploads/users/" & row.ID & ".jpg";
        staffMember["RatingToken"] = row.RatingToken ?: "";
        arrayAppend(staff, staffMember);
    }

    // Build response (use ordered structs to preserve key casing)
    customer = structNew("ordered");
    customer["UserID"] = qOrder.UserID;
    customer["FirstName"] = qOrder.FirstName;
    customer["LastName"] = qOrder.LastName;
    customer["Phone"] = qOrder.ContactNumber;
    customer["Email"] = qOrder.EmailAddress;

    servicePoint = structNew("ordered");
    servicePoint["ServicePointID"] = qOrder.ServicePointID;
    servicePoint["Name"] = qOrder.Name;
    servicePoint["TypeID"] = qOrder.TypeID;

    order = structNew("ordered");
    order["OrderID"] = qOrder.ID;
    order["BusinessID"] = qOrder.BusinessID;
    order["Name"] = qOrder.Name ?: "";
    order["Status"] = qOrder.StatusID;
    order["StatusText"] = getStatusText(qOrder.StatusID);
    order["OrderTypeID"] = qOrder.OrderTypeID ?: 0;
    order["OrderTypeName"] = getOrderTypeName(qOrder.OrderTypeID ?: 0);
    order["Subtotal"] = subtotal;
    order["Tax"] = tax;
    order["Tip"] = tip;
    order["Total"] = total;
    order["Notes"] = qOrder.Remarks;
    order["CreatedOn"] = dateTimeFormat(qOrder.AddedOn, "yyyy-mm-dd HH:nn:ss");
    order["SubmittedOn"] = len(qOrder.SubmittedOn) ? dateTimeFormat(qOrder.SubmittedOn, "yyyy-mm-dd HH:nn:ss") : "";
    order["UpdatedOn"] = len(qOrder.LastEditedOn) ? dateTimeFormat(qOrder.LastEditedOn, "yyyy-mm-dd HH:nn:ss") : "";
    order["Customer"] = customer;
    order["ServicePoint"] = servicePoint;
    order["LineItems"] = lineItems;
    order["Staff"] = staff;

    response["OK"] = true;
    response["ORDER"] = order;

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

writeOutput(serializeJSON(response));

// Helper functions
function getStatusText(status) {
    switch (status) {
        case 0: return "Cart";
        case 1: return "Submitted";
        case 2: return "In Progress";
        case 3: return "Ready";
        case 4: return "On the Way";
        case 5: return "Complete";
        case 6: return "Cancelled";
        case 7: return "Deleted";
        default: return "Unknown";
    }
}

function getOrderTypeName(orderType) {
    switch (orderType) {
        case 1: return "Dine-in";
        case 2: return "Takeaway";
        case 3: return "Delivery";
        default: return "Unknown";
    }
}
</cfscript>
