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

<cfscript>
/**
 * Verify OTP and activate user account
 *
 * POST: { "uuid": "...", "otp": "123456" }
 *
 * Returns: { OK: true, UserID: 123, Token: "...", NeedsProfile: true/false }
 *
 * On success, marks phone as verified and returns auth token.
 * NeedsProfile indicates if user still needs to provide name/email.
 */

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();
    userUUID = structKeyExists(data, "uuid") ? trim(data.uuid) : "";
    otp = structKeyExists(data, "otp") ? trim(data.otp) : "";

    if (!len(userUUID) || !len(otp)) {
        apiAbort({ "OK": false, "ERROR": "missing_fields", "MESSAGE": "UUID and OTP are required" });
    }

    // Find unverified user with matching UUID and OTP
    // Magic OTP only bypasses Twilio SMS (in sendOTP.cfm), not OTP verification
    qUser = queryTimed("
        SELECT ID, FirstName, LastName, EmailAddress, IsEmailVerified
        FROM Users
        WHERE UUID = :uuid
          AND MobileVerifyCode = :otp
          AND IsContactVerified = 0
        LIMIT 1
    ", {
        uuid: { value: userUUID, cfsqltype: "cf_sql_varchar" },
        otp: { value: otp, cfsqltype: "cf_sql_varchar" }
    }, { datasource: "payfrit" });

    if (qUser.recordCount == 0) {
        // Check if UUID exists but OTP is wrong
        qCheck = queryTimed("
            SELECT ID FROM Users WHERE UUID = :uuid AND IsContactVerified = 0
        ", { uuid: { value: userUUID, cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" });

        if (qCheck.recordCount > 0) {
            apiAbort({ "OK": false, "ERROR": "invalid_otp", "MESSAGE": "Invalid verification code. Please try again." });
        } else {
            apiAbort({ "OK": false, "ERROR": "expired", "MESSAGE": "Verification expired. Please request a new code." });
        }
    }

    // Clear the OTP code (one-time use) but DON'T mark as verified yet
    // Account will be marked verified after profile completion
    queryTimed("
        UPDATE Users
        SET MobileVerifyCode = ''
        WHERE ID = :userId
    ", { userId: { value: qUser.ID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" });

    // Create auth token (needed for completeProfile call)
    token = replace(createUUID(), "-", "", "all");
    queryTimed("
        INSERT INTO UserTokens (UserID, Token) VALUES (:userId, :token)
    ", {
        userId: { value: qUser.ID, cfsqltype: "cf_sql_integer" },
        token: { value: token, cfsqltype: "cf_sql_varchar" }
    }, { datasource: "payfrit" });

    // Check if profile is complete (has first name)
    // For new signups, this will always be true
    needsProfile = !len(trim(qUser.FirstName));

    try{logPerf(0);}catch(any e){}
    writeOutput(serializeJSON({
        "OK": true,
        "UserID": qUser.ID,
        "Token": token,
        "NeedsProfile": needsProfile,
        "FirstName": qUser.FirstName ?: "",
        "IsEmailVerified": qUser.IsEmailVerified == 1
    }));

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