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

<cfscript>
/**
 * Send OTP to phone number for signup
 *
 * POST: { "phone": "5551234567" }
 *
 * Returns: { OK: true, UUID: "..." } or { OK: false, ERROR: "..." }
 *
 * If phone already has verified account, returns error.
 * If phone has unverified account, resends OTP.
 * Otherwise creates new user record with OTP.
 */

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 {};
}

function normalizePhone(required string p) {
    var x = trim(arguments.p);
    x = reReplace(x, "[^0-9]", "", "all");
    // Remove leading 1 if 11 digits
    if (len(x) == 11 && left(x, 1) == "1") {
        x = right(x, 10);
    }
    return x;
}

function generateOTP() {
    return randRange(100000, 999999);
}

try {
    data = readJsonBody();
    phone = structKeyExists(data, "phone") ? normalizePhone(data.phone) : "";

    if (len(phone) != 10) {
        apiAbort({ "OK": false, "ERROR": "invalid_phone", "MESSAGE": "Please enter a valid 10-digit phone number" });
    }

    // Check if phone already has a COMPLETE account (verified AND has profile info)
    // An account is only "complete" if they have a first name (meaning they finished signup)
    qExisting = queryExecute("
        SELECT ID, UUID, FirstName
        FROM Users
        WHERE ContactNumber = :phone
          AND IsContactVerified > 0
          AND FirstName IS NOT NULL
          AND LENGTH(TRIM(FirstName)) > 0
        LIMIT 1
    ", { phone: { value: phone, cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" });

    if (qExisting.recordCount > 0) {
        apiAbort({ "OK": false, "ERROR": "phone_exists", "MESSAGE": "This phone number already has an account. Please login instead." });
    }

    // Check for incomplete account with this phone (verified but no profile, OR unverified)
    // These accounts can be reused for signup
    qIncomplete = queryExecute("
        SELECT ID, UUID
        FROM Users
        WHERE ContactNumber = :phone
          AND (IsContactVerified = 0 OR FirstName IS NULL OR LENGTH(TRIM(FirstName)) = 0)
        LIMIT 1
    ", { phone: { value: phone, cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" });

    otp = generateOTP();
    if (structKeyExists(application, "MAGIC_OTP_ENABLED") && application.MAGIC_OTP_ENABLED
        && structKeyExists(application, "MAGIC_OTP_CODE") && len(application.MAGIC_OTP_CODE)) {
        otp = application.MAGIC_OTP_CODE;
    }
    userUUID = "";

    if (qIncomplete.recordCount > 0) {
        // Update existing incomplete record with new OTP and reset for re-registration
        userUUID = qIncomplete.UUID;
        queryExecute("
            UPDATE Users
            SET MobileVerifyCode = :otp,
                IsContactVerified = 0,
                IsActive = 0
            WHERE ID = :userId
        ", {
            otp: { value: otp, cfsqltype: "cf_sql_varchar" },
            userId: { value: qIncomplete.ID, cfsqltype: "cf_sql_integer" }
        }, { datasource: "payfrit" });
    } else {
        // Create new user record
        userUUID = replace(createUUID(), "-", "", "all");
        queryExecute("
            INSERT INTO Users (
                ContactNumber,
                UUID,
                MobileVerifyCode,
                IsContactVerified,
                IsEmailVerified,
                IsActive,
                AddedOn,
                Password,
                PromoCode
            ) VALUES (
                :phone,
                :uuid,
                :otp,
                0,
                0,
                0,
                :addedOn,
                '',
                :promoCode
            )
        ", {
            phone: { value: phone, cfsqltype: "cf_sql_varchar" },
            uuid: { value: userUUID, cfsqltype: "cf_sql_varchar" },
            otp: { value: otp, cfsqltype: "cf_sql_varchar" },
            addedOn: { value: now(), cfsqltype: "cf_sql_timestamp" },
            promoCode: { value: randRange(1000000, 9999999), cfsqltype: "cf_sql_varchar" }
        }, { datasource: "payfrit" });
    }

    // Send OTP via Twilio (skip on dev server)
    smsMessage = "Code saved (SMS skipped in dev)";
    isDev = findNoCase("dev.payfrit.com", cgi.SERVER_NAME) > 0;

    if (!isDev && structKeyExists(application, "twilioObj")) {
        try {
            smsResult = application.twilioObj.sendSMS(
                recipientNumber: "+1" & phone,
                messageBody: "Your Payfrit verification code is: " & otp
            );
            smsMessage = smsResult.success ? "Verification code sent" : "SMS failed - please try again";
        } catch (any smsErr) {
            smsMessage = "SMS error: " & smsErr.message;
        }
    }

    writeOutput(serializeJSON({
        "OK": true,
        "UUID": userUUID,
        "MESSAGE": smsMessage,
        "DEV_OTP": otp
    }));

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