<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">
<cfcontent type="application/json; charset=utf-8" reset="true">

<cfscript>
// Customer initiates a chat with staff
// Input: BusinessID, ServicePointID, OrderID (optional), UserID (optional), Message (optional initial message)
// Output: { OK: true, TaskID: ... }

function apiAbort(required struct payload) {
    writeOutput(serializeJSON(payload));
    abort;
}

function readJsonBody() {
    var raw = getHttpRequestData().content;
    if (isNull(raw)) raw = "";
    if (!len(trim(raw))) return {};
    try {
        var data = deserializeJSON(raw);
        if (isStruct(data)) return data;
    } catch (any e) {}
    return {};
}

try {
    data = readJsonBody();
    businessID = val(structKeyExists(data, "BusinessID") ? data.BusinessID : 0);
    servicePointID = val(structKeyExists(data, "ServicePointID") ? data.ServicePointID : 0);
    orderID = val(structKeyExists(data, "OrderID") ? data.OrderID : 0);
    initialMessage = trim(structKeyExists(data, "Message") ? data.Message : "");
    userID = val(structKeyExists(data, "UserID") ? data.UserID : 0);

    if (businessID == 0) {
        apiAbort({ "OK": false, "ERROR": "missing_params", "MESSAGE": "BusinessID is required" });
    }

    // Look up "Chat With Staff" task type for this business
    ttQuery = queryTimed("
        SELECT ID FROM tt_TaskTypes
        WHERE BusinessID = :businessID AND Name LIKE '%Chat%'
        LIMIT 1
    ", { businessID: { value: businessID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" });
    chatTaskTypeID = ttQuery.recordCount > 0 ? ttQuery.ID : 0;

    // ServicePointID = 0 is allowed for remote chats (non-dine-in users)
    // In that case, we use userID to match existing chats

    // Check for existing open chat at this service point
    forceNew = structKeyExists(data, "ForceNew") && data.ForceNew == true;

    if (!forceNew) {
        // Look for any active chat for this business
        // Check by: order match, service point match, OR user match (for remote chats)
        existingChat = queryTimed("
            SELECT t.ID, t.CreatedOn,
                   (SELECT MAX(cm.CreatedOn) FROM ChatMessages cm WHERE cm.TaskID = t.ID) as LastMessageTime
            FROM Tasks t
            LEFT JOIN ChatMessages cm2 ON cm2.TaskID = t.ID AND cm2.SenderUserID = :userID
            WHERE t.BusinessID = :businessID
              AND (t.TaskTypeID = :chatTypeID OR t.Title LIKE 'Chat%')
              AND t.CompletedOn IS NULL
              AND (
                  (t.OrderID = :orderID AND :orderID > 0)
                  OR (t.SourceType = 'servicepoint' AND t.SourceID = :servicePointID AND :servicePointID > 0)
                  OR (t.SourceType = 'user' AND t.SourceID = :userID AND :userID > 0)
                  OR (cm2.SenderUserID = :userID AND :userID > 0)
              )
            ORDER BY t.CreatedOn DESC
            LIMIT 1
        ", {
            businessID: { value: businessID, cfsqltype: "cf_sql_integer" },
            chatTypeID: { value: chatTaskTypeID, cfsqltype: "cf_sql_integer" },
            servicePointID: { value: servicePointID, cfsqltype: "cf_sql_integer" },
            orderID: { value: orderID, cfsqltype: "cf_sql_integer" },
            userID: { value: userID, cfsqltype: "cf_sql_integer" }
        }, { datasource: "payfrit" });

        if (existingChat.recordCount > 0) {
            // Check if chat is stale (more than 30 minutes since last message, or 30 min since creation if no messages)
            lastActivity = existingChat.LastMessageTime;
            if (isNull(lastActivity) || !isDate(lastActivity)) {
                lastActivity = existingChat.CreatedOn;
            }
            chatAge = dateDiff("n", lastActivity, now());

            if (chatAge > 30) {
                // Auto-close stale chat
                queryTimed("
                    UPDATE Tasks SET CompletedOn = NOW()
                    WHERE TaskID = :taskID
                ", { taskID: { value: existingChat.ID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" });
            } else {
                // Return existing chat
                apiAbort({
                    "OK": true,
                    "TaskID": existingChat.ID,
                    "MESSAGE": "Rejoined existing chat",
                    "EXISTING": true
                });
            }
        }
    }

    // Get service point info (table name) - only if dine-in
    tableName = "";
    if (servicePointID > 0) {
        spQuery = queryTimed("
            SELECT Name FROM ServicePoints WHERE ID = :spID
        ", { spID: { value: servicePointID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" });
        tableName = spQuery.recordCount ? spQuery.Name : "Table ##" & servicePointID;
    }

    // Get user name if available
    userName = "";
    if (userID > 0) {
        userQuery = queryTimed("
            SELECT FirstName FROM Users WHERE ID = :userID
        ", { userID: { value: userID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" });
        if (userQuery.recordCount && len(trim(userQuery.FirstName))) {
            userName = userQuery.FirstName;
        }
    }

    // Create task title - different format for dine-in vs remote
    if (servicePointID > 0) {
        // Dine-in: "Chat - UserName (TableName)" or "Chat - TableName"
        taskTitle = "Chat - " & tableName;
        if (len(userName)) {
            taskTitle = "Chat - " & userName & " (" & tableName & ")";
        }
    } else {
        // Remote: "Chat - UserName (Remote)" or "Remote Chat"
        if (len(userName)) {
            taskTitle = "Chat - " & userName & " (Remote)";
        } else {
            taskTitle = "Remote Chat";
        }
    }

    taskDetails = "Customer initiated chat";
    if (len(initialMessage)) {
        taskDetails = initialMessage;
    }

    // Look up or create a "Chat" category for this business
    catQuery = queryTimed("
        SELECT ID FROM TaskCategories
        WHERE BusinessID = :businessID AND Name = 'Chat'
        LIMIT 1
    ", { businessID: { value: businessID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" });

    if (catQuery.recordCount == 0) {
        // Create the category (blue color for chat)
        queryTimed("
            INSERT INTO TaskCategories (BusinessID, Name, Color)
            VALUES (:businessID, 'Chat', '##2196F3')
        ", { businessID: { value: businessID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" });

        catResult = queryTimed("SELECT LAST_INSERT_ID() as newID", [], { datasource: "payfrit" });
        categoryID = catResult.newID;
    } else {
        categoryID = catQuery.ID;
    }

    // Determine source type and ID based on dine-in vs remote
    if (servicePointID > 0) {
        sourceType = "servicepoint";
        sourceID = servicePointID;
    } else {
        sourceType = "user";
        sourceID = userID;
    }

    // Insert task with business-specific Chat task type
    queryTimed("
        INSERT INTO Tasks (
            BusinessID,
            CategoryID,
            OrderID,
            TaskTypeID,
            Title,
            Details,
            ClaimedByUserID,
            SourceType,
            SourceID,
            CreatedOn
        ) VALUES (
            :businessID,
            :categoryID,
            :orderID,
            :taskTypeID,
            :title,
            :details,
            0,
            :sourceType,
            :sourceID,
            NOW()
        )
    ", {
        businessID: { value: businessID, cfsqltype: "cf_sql_integer" },
        categoryID: { value: categoryID, cfsqltype: "cf_sql_integer" },
        orderID: { value: orderID > 0 ? orderID : javaCast("null", ""), cfsqltype: "cf_sql_integer", null: orderID == 0 },
        taskTypeID: { value: chatTaskTypeID, cfsqltype: "cf_sql_integer", null: chatTaskTypeID == 0 },
        title: { value: taskTitle, cfsqltype: "cf_sql_varchar" },
        details: { value: taskDetails, cfsqltype: "cf_sql_varchar" },
        sourceType: { value: sourceType, cfsqltype: "cf_sql_varchar" },
        sourceID: { value: sourceID, cfsqltype: "cf_sql_integer" }
    }, { datasource: "payfrit" });

    // Get the new task ID
    result = queryTimed("SELECT LAST_INSERT_ID() as newID", [], { datasource: "payfrit" });
    taskID = result.newID;

    // If there's an initial message, save it
    if (len(initialMessage) && userID > 0) {
        queryTimed("
            INSERT INTO ChatMessages (TaskID, SenderUserID, SenderType, MessageBody)
            VALUES (:taskID, :userID, 'customer', :message)
        ", {
            taskID: { value: taskID, cfsqltype: "cf_sql_integer" },
            userID: { value: userID, cfsqltype: "cf_sql_integer" },
            message: { value: initialMessage, cfsqltype: "cf_sql_varchar" }
        }, { datasource: "payfrit" });
    }

    apiAbort({
        "OK": true,
        "TaskID": taskID,
        "MESSAGE": "Chat started"
    });

} catch (any e) {
    apiAbort({
        "OK": false,
        "ERROR": "server_error",
        "MESSAGE": e.message
    });
}
</cfscript>
