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

<cfscript>
/**
 * Time Travel Endpoint - DEV ONLY
 *
 * Allows manipulation of timestamps for testing time-based features.
 * Examples: token expiry, trial periods, scheduled events.
 *
 * POST: { "action": "expireTokens", "userId": 123 }
 * POST: { "action": "setUserCreated", "userId": 123, "daysAgo": 30 }
 */

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

// SAFETY: Only allow on dev environment
if (!structKeyExists(application, "isDevEnvironment") || !application.isDevEnvironment) {
    apiAbort({
        "OK": false,
        "ERROR": "forbidden",
        "MESSAGE": "This endpoint is only available in development"
    });
}

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 {
    if (cgi.request_method != "POST") {
        apiAbort({
            "OK": false,
            "ERROR": "method_not_allowed",
            "MESSAGE": "POST required"
        });
    }

    data = readJsonBody();
    action = structKeyExists(data, "action") ? lcase(data.action) : "";

    switch (action) {
        case "expiretokens":
            // Expire all tokens for a user (simulate session timeout)
            if (!structKeyExists(data, "userId")) {
                apiAbort({ "OK": false, "ERROR": "missing_userId" });
            }

            queryTimed("
                UPDATE UserTokens
                SET CreatedAt = DATE_SUB(NOW(), INTERVAL 30 DAY)
                WHERE UserID = :userId
            ", {
                userId: { value: data.userId, cfsqltype: "cf_sql_integer" }
            }, { datasource: "payfrit" });

            writeOutput(serializeJSON({
                "OK": true,
                "message": "Tokens expired for user " & data.userId
            }));
            break;

        case "setusercreated":
            // Backdate user creation (for testing trial periods, etc)
            if (!structKeyExists(data, "userId") || !structKeyExists(data, "daysAgo")) {
                apiAbort({ "OK": false, "ERROR": "missing_userId_or_daysAgo" });
            }

            queryTimed("
                UPDATE Users
                SET AddedOn = DATE_SUB(NOW(), INTERVAL :days DAY)
                WHERE UserID = :userId
            ", {
                userId: { value: data.userId, cfsqltype: "cf_sql_integer" },
                days: { value: data.daysAgo, cfsqltype: "cf_sql_integer" }
            }, { datasource: "payfrit" });

            writeOutput(serializeJSON({
                "OK": true,
                "message": "User " & data.userId & " created date set to " & data.daysAgo & " days ago"
            }));
            break;

        case "clearotps":
            // Clear all OTPs (force re-request)
            queryTimed("
                UPDATE Users SET MobileVerifyCode = ''
            ", {}, { datasource: "payfrit" });

            writeOutput(serializeJSON({
                "OK": true,
                "message": "All OTPs cleared"
            }));
            break;

        case "resetuser":
            // Reset a user to unverified state (for retesting signup)
            if (!structKeyExists(data, "phone")) {
                apiAbort({ "OK": false, "ERROR": "missing_phone" });
            }

            queryTimed("
                UPDATE Users
                SET IsContactVerified = 0,
                    IsActive = 0,
                    FirstName = NULL,
                    LastName = NULL,
                    MobileVerifyCode = '123456'
                WHERE ContactNumber = :phone
            ", {
                phone: { value: data.phone, cfsqltype: "cf_sql_varchar" }
            }, { datasource: "payfrit" });

            writeOutput(serializeJSON({
                "OK": true,
                "message": "User with phone " & data.phone & " reset to unverified"
            }));
            break;

        default:
            apiAbort({
                "OK": false,
                "ERROR": "unknown_action",
                "MESSAGE": "Valid actions: expireTokens, setUserCreated, clearOTPs, resetUser"
            });
    }

} catch (any e) {
    apiAbort({
        "OK": false,
        "ERROR": "server_error",
        "MESSAGE": application.showDetailedErrors ? e.message : "An error occurred"
    });
}
</cfscript>
