<cfscript>
/**
 * Environment Configuration
 *
 * Controls dev vs production behavior.
 * This file should have DIFFERENT values on dev vs biz servers.
 *
 * dev.payfrit.com: isDevEnvironment = true
 * biz.payfrit.com: isDevEnvironment = false
 */

// ============================================
// ENVIRONMENT FLAG - CHANGE PER SERVER
// ============================================
isDevEnvironment = true;  // Set to FALSE on biz.payfrit.com

// ============================================
// ERROR HANDLING
// ============================================
// Dev: Show full stack traces
// Prod: Show generic "server error" message
application.showDetailedErrors = isDevEnvironment;

// ============================================
// DEBUG LOGGING
// ============================================
// Dev: Log all API requests/responses
// Prod: Minimal logging
application.debugLogging = isDevEnvironment;
application.logDirectory = expandPath("/api/logs/");

// ============================================
// RATE LIMITING
// ============================================
// Dev: No rate limits
// Prod: Enforce rate limits
application.enableRateLimiting = !isDevEnvironment;
application.rateLimitPerMinute = 60;  // requests per minute per IP

// ============================================
// EMAIL HANDLING
// ============================================
// Dev: Send all emails to catch-all address
// Prod: Send to real recipients
application.emailCatchAll = isDevEnvironment ? "dev-emails@payfrit.com" : "";
application.emailEnabled = !isDevEnvironment ? true : false;  // Disable emails on dev entirely

// ============================================
// MAGIC OTP (for testing)
// ============================================
// Dev: Allow magic phone number to bypass OTP
// Prod: Disabled
application.MAGIC_OTP_ENABLED = isDevEnvironment;
application.MAGIC_OTP_CODE = "123456";
application.MAGIC_PHONE_NUMBERS = ["5555555555", "0000000000"];

// ============================================
// STRIPE MODE
// ============================================
// Already handled in stripe.cfm, but good to have reference
application.stripeTestMode = isDevEnvironment;

// ============================================
// API RESPONSE EXTRAS
// ============================================
// Dev: Include debug info in API responses (timing, queries, etc)
// Prod: Minimal responses
application.includeDebugInResponse = isDevEnvironment;

// ============================================
// SESSION/TOKEN SETTINGS
// ============================================
// Dev: Longer token expiry for easier testing
// Prod: Normal expiry
application.tokenExpiryHours = isDevEnvironment ? 720 : 24;  // 30 days vs 1 day

// ============================================
// HELPER FUNCTIONS
// ============================================
function isDev() {
    return structKeyExists(application, "isDevEnvironment") && application.isDevEnvironment;
}

function logDebug(message, data = {}) {
    if (!application.debugLogging) return;

    var logFile = application.logDirectory & "debug_" & dateFormat(now(), "yyyy-mm-dd") & ".log";
    var logLine = "[" & timeFormat(now(), "HH:mm:ss") & "] " & message;

    if (!structIsEmpty(data)) {
        logLine &= " | " & serializeJSON(data);
    }

    try {
        if (!directoryExists(application.logDirectory)) {
            directoryCreate(application.logDirectory);
        }
        fileAppend(logFile, logLine & chr(10));
    } catch (any e) {
        // Silent fail - don't break app if logging fails
    }
}

function apiError(message, detail = "", statusCode = 500) {
    var response = {
        "OK": false,
        "ERROR": "server_error",
        "MESSAGE": application.showDetailedErrors ? message : "An error occurred"
    };

    if (application.showDetailedErrors && len(detail)) {
        response["DETAIL"] = detail;
        response["STACK"] = "";
    }

    return response;
}

// ============================================
// PERFORMANCE PROFILING
// ============================================
application.perfEnabled = true;

if (!structKeyExists(application, "perfBuffer")) {
    application.perfBuffer = [];
}

/**
 * Drop-in replacement for queryExecute() that tracks query count and time.
 * Opt-in: use in endpoints where you want accurate DB time breakdown.
 */
function queryTimed(required string sql, any params = [], struct options = {}) {
    if (!structKeyExists(request, "_perf_queryCount")) {
        request._perf_queryCount = 0;
        request._perf_queryTimeMs = 0;
    }

    var t = getTickCount();
    var result = queryExecute(arguments.sql, arguments.params, arguments.options);
    var elapsed = getTickCount() - t;

    request._perf_queryCount++;
    request._perf_queryTimeMs += elapsed;

    return result;
}

/**
 * Flush the in-memory perf buffer to the ApiPerfLogs MySQL table.
 * Thread-safe: duplicates buffer under lock, then inserts outside lock.
 */
function flushPerfBuffer() {
    // Auto-create table if it doesn't exist
    if (!structKeyExists(application, "_perfTableChecked")) {
        try {
            queryExecute("
                CREATE TABLE IF NOT EXISTS ApiPerfLogs (
                    ID INT AUTO_INCREMENT PRIMARY KEY,
                    Endpoint VARCHAR(255) NOT NULL,
                    TotalMs INT NOT NULL DEFAULT 0,
                    DbMs INT NOT NULL DEFAULT 0,
                    AppMs INT NOT NULL DEFAULT 0,
                    QueryCount INT NOT NULL DEFAULT 0,
                    ResponseBytes INT NOT NULL DEFAULT 0,
                    BusinessID INT NOT NULL DEFAULT 0,
                    UserID INT NOT NULL DEFAULT 0,
                    LoggedAt DATETIME NOT NULL,
                    INDEX idx_loggedat (LoggedAt),
                    INDEX idx_endpoint (Endpoint)
                ) ENGINE=InnoDB
            ", {}, { datasource: "payfrit" });
            application._perfTableChecked = true;
        } catch (any e) {}
    }

    var batch = [];

    lock name="payfrit_perfBuffer" timeout="2" type="exclusive" {
        if (structKeyExists(application, "perfBuffer")) {
            batch = duplicate(application.perfBuffer);
            application.perfBuffer = [];
        }
    }

    if (arrayLen(batch) == 0) return;

    try {

        var sql = "INSERT INTO ApiPerfLogs (Endpoint, TotalMs, DbMs, AppMs, QueryCount, ResponseBytes, BusinessID, UserID, LoggedAt) VALUES ";
        var rows = [];

        for (var m in batch) {
            arrayAppend(rows,
                "('" & replace(m.endpoint, "'", "''", "all") & "',"
                & val(m.totalMs) & "," & val(m.dbMs) & "," & val(m.appMs) & ","
                & val(m.queryCount) & "," & val(m.responseBytes) & ","
                & val(m.businessId) & "," & val(m.userId) & ",'"
                & m.loggedAt & "')"
            );
        }

        sql &= arrayToList(rows, ",");
        queryExecute(sql, {}, { datasource: "payfrit" });

        // Cleanup old data (1% chance per flush)
        if (randRange(1, 100) == 1) {
            queryExecute(
                "DELETE FROM ApiPerfLogs WHERE LoggedAt < DATE_SUB(NOW(), INTERVAL 30 DAY)",
                {}, { datasource: "payfrit" }
            );
        }
    } catch (any e) {
        // Silent fail - never break the app for profiling
    }
}

/**
 * Log performance metrics for the current request.
 * Called automatically from apiAbort().
 */
function logPerf(numeric responseBytes = 0) {
    if (!structKeyExists(application, "perfEnabled") || !application.perfEnabled) return;
    if (!structKeyExists(request, "_perf_start")) return;

    // Safety valve: don't let buffer grow unbounded if flush is failing
    if (structKeyExists(application, "perfBuffer") && arrayLen(application.perfBuffer) > 1000) return;

    var totalMs = getTickCount() - request._perf_start;
    var dbMs = structKeyExists(request, "_perf_queryTimeMs") ? request._perf_queryTimeMs : 0;

    var metric = {
        endpoint: structKeyExists(request, "_api_path") ? request._api_path : "unknown",
        totalMs: totalMs,
        dbMs: dbMs,
        appMs: totalMs - dbMs,
        queryCount: structKeyExists(request, "_perf_queryCount") ? request._perf_queryCount : 0,
        responseBytes: arguments.responseBytes,
        businessId: structKeyExists(request, "BusinessID") ? val(request.BusinessID) : 0,
        userId: structKeyExists(request, "UserID") ? val(request.UserID) : 0,
        loggedAt: dateTimeFormat(now(), "yyyy-mm-dd HH:nn:ss")
    };

    var shouldFlush = false;

    lock name="payfrit_perfBuffer" timeout="1" type="exclusive" {
        arrayAppend(application.perfBuffer, metric);
        if (arrayLen(application.perfBuffer) >= 100) {
            shouldFlush = true;
        }
    }

    if (shouldFlush) {
        thread name="perfFlush_#createUUID()#" {
            flushPerfBuffer();
        }
    }
}

// Store in application scope
application.isDevEnvironment = isDevEnvironment;
application.baseUrl = isDevEnvironment ? "https://dev.payfrit.com" : "https://biz.payfrit.com";
application.isDev = isDev;
application.logDebug = logDebug;
application.apiError = apiError;
application.queryTimed = queryTimed;
application.logPerf = logPerf;
application.flushPerfBuffer = flushPerfBuffer;
</cfscript>
