<?php
declare(strict_types=1);

header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-User-Id');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    exit;
}

require_once __DIR__ . '/../../includes/config.php';

$authHelper = __DIR__ . '/../../includes/auth_helper.php';
if (file_exists($authHelper)) {
    require_once $authHelper;
}

function ld_json(array $data, int $code = 200): void {
    http_response_code($code);
    echo json_encode($data, JSON_UNESCAPED_SLASHES);
    exit;
}

function ld_pdo(): PDO {
    global $pdo, $conn, $db;

    if (isset($pdo) && $pdo instanceof PDO) return $pdo;
    if (isset($conn) && $conn instanceof PDO) return $conn;
    if (isset($db) && $db instanceof PDO) return $db;

    ld_json(['success' => false, 'message' => 'Database connection not found.'], 500);
}

function ld_body(): array {
    $raw = file_get_contents('php://input');
    $json = json_decode($raw ?: '', true);
    if (is_array($json)) return $json;
    return $_POST ?: [];
}

function ld_bearer_token(): string {
    $auth = $_SERVER['HTTP_AUTHORIZATION']
        ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION']
        ?? '';

    if (!$auth && function_exists('getallheaders')) {
        $headers = getallheaders();
        foreach ($headers as $k => $v) {
            if (strtolower($k) === 'authorization') {
                $auth = $v;
                break;
            }
        }
    }

    if (preg_match('/Bearer\s+(.+)/i', $auth, $m)) {
        return trim($m[1]);
    }

    return '';
}

function ld_extract_user_id_from_auth_result($result): int {
    if (is_numeric($result) && (int)$result > 0) {
        return (int)$result;
    }

    if (is_array($result)) {
        foreach (['id', 'user_id', 'uid'] as $key) {
            if (isset($result[$key]) && is_numeric($result[$key]) && (int)$result[$key] > 0) {
                return (int)$result[$key];
            }
        }

        if (isset($result['user']) && is_array($result['user'])) {
            foreach (['id', 'user_id', 'uid'] as $key) {
                if (isset($result['user'][$key]) && is_numeric($result['user'][$key]) && (int)$result['user'][$key] > 0) {
                    return (int)$result['user'][$key];
                }
            }
        }
    }

    if (is_object($result)) {
        foreach (['id', 'user_id', 'uid'] as $key) {
            if (isset($result->$key) && is_numeric($result->$key) && (int)$result->$key > 0) {
                return (int)$result->$key;
            }
        }

        if (isset($result->user) && is_object($result->user)) {
            foreach (['id', 'user_id', 'uid'] as $key) {
                if (isset($result->user->$key) && is_numeric($result->user->$key) && (int)$result->user->$key > 0) {
                    return (int)$result->user->$key;
                }
            }
        }
    }

    return 0;
}

function ld_current_user_id(): int {
    $token = ld_bearer_token();

    if ($token === '') {
        ld_json([
            'success' => false,
            'message' => 'Unauthorized. Missing Authorization: Bearer token.'
        ], 401);
    }

    $pdo = ld_pdo();

    /*
     * Try to reuse your existing auth helper functions.
     * This keeps Paystack using the same login token system as wallet/profile/matchmaking.
     */
    $candidateFns = [
        'getAuthenticatedUserId',
        'getCurrentUserId',
        'current_user_id',
        'require_auth_user_id',
        'requireAuthUserId',
        'authenticateUserId',
        'get_user_id_from_token',
        'getUserIdFromToken',
        'validateTokenAndGetUserId',
        'verifyTokenAndGetUserId',
        'require_auth',
        'requireAuth',
        'authenticate_user',
        'authenticateUser',
        'getAuthenticatedUser',
        'getCurrentUser',
        'current_user',
    ];

    foreach ($candidateFns as $fn) {
        if (!function_exists($fn)) {
            continue;
        }

        try {
            $ref = new ReflectionFunction($fn);
            $required = $ref->getNumberOfRequiredParameters();
            $total = $ref->getNumberOfParameters();

            $result = null;

            if ($required === 0) {
                $result = $fn();
            } elseif ($required <= 1) {
                // Try token first, then PDO. Different helper files use different signatures.
                try {
                    $result = $fn($token);
                } catch (Throwable $inner) {
                    $result = $fn($pdo);
                }
            } elseif ($required <= 2) {
                try {
                    $result = $fn($token, $pdo);
                } catch (Throwable $inner) {
                    $result = $fn($pdo, $token);
                }
            } else {
                continue;
            }

            $id = ld_extract_user_id_from_auth_result($result);
            if ($id > 0) {
                return $id;
            }
        } catch (Throwable $e) {
            // Try the next known auth helper.
        }
    }

    ld_json([
        'success' => false,
        'message' => 'Unauthorized. Token could not be validated by auth helper.'
    ], 401);
}

function ld_columns(PDO $pdo, string $table): array {
    static $cache = [];
    if (isset($cache[$table])) return $cache[$table];

    try {
        $stmt = $pdo->query("SHOW COLUMNS FROM `$table`");
        $cols = [];
        foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
            $cols[] = $row['Field'];
        }
        return $cache[$table] = $cols;
    } catch (Throwable $e) {
        return $cache[$table] = [];
    }
}

function ld_table_exists(PDO $pdo, string $table): bool {
    try {
        $stmt = $pdo->prepare("SHOW TABLES LIKE ?");
        $stmt->execute([$table]);
        return (bool)$stmt->fetchColumn();
    } catch (Throwable $e) {
        return false;
    }
}

function ld_paystack_secret(?string $countryCode = null): string {
    $countryCode = strtoupper(trim((string)$countryCode));

    if ($countryCode === 'GH') {
        $secret = getenv('PAYSTACK_SECRET_KEY_GH');
        if (!$secret) {
            ld_json(['success' => false, 'message' => 'PAYSTACK_SECRET_KEY_GH is missing on server.'], 500);
        }
        return $secret;
    }

    if ($countryCode === 'NG') {
        $secret = getenv('PAYSTACK_SECRET_KEY_NG') ?: getenv('PAYSTACK_SECRET_KEY');
        if (!$secret) {
            ld_json(['success' => false, 'message' => 'PAYSTACK_SECRET_KEY_NG is missing on server.'], 500);
        }
        return $secret;
    }

    $secret = getenv('PAYSTACK_SECRET_KEY');
    if (!$secret) {
        ld_json(['success' => false, 'message' => 'PAYSTACK_SECRET_KEY is missing on server.'], 500);
    }
    return $secret;
}

function ld_paystack_request(string $method, string $path, ?array $payload = null, ?string $countryCode = null): array {
    $secret = ld_paystack_secret($countryCode);
    $url = 'https://api.paystack.co' . $path;

    $ch = curl_init($url);
    $headers = [
        'Authorization: Bearer ' . $secret,
        'Content-Type: application/json',
        'Accept: application/json',
    ];

    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST => strtoupper($method),
        CURLOPT_HTTPHEADER => $headers,
        CURLOPT_TIMEOUT => 30,
    ]);

    if ($payload !== null) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
    }

    $response = curl_exec($ch);
    $err = curl_error($ch);
    $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($response === false) {
        return ['status' => false, 'message' => 'Curl error: ' . $err, 'http_code' => $http];
    }

    $json = json_decode($response, true);
    if (!is_array($json)) {
        return ['status' => false, 'message' => 'Invalid Paystack response', 'raw' => $response, 'http_code' => $http];
    }

    $json['http_code'] = $http;
    return $json;
}

function ld_get_user(PDO $pdo, int $userId): array {
    $cols = ld_columns($pdo, 'users');

    $select = ['id'];
    $select[] = in_array('email', $cols, true) ? 'email' : "NULL AS email";
    $select[] = in_array('country_code', $cols, true) ? 'country_code' : "NULL AS country_code";

    $sql = "SELECT " . implode(',', $select) . " FROM users WHERE id = ? LIMIT 1";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$userId]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$user) {
        ld_json(['success' => false, 'message' => 'User not found.'], 404);
    }

    if (empty($user['email'])) {
        $user['email'] = 'player' . $userId . '@ludodragon.local';
    }

    return $user;
}

function ld_get_gateway_config(PDO $pdo, string $countryCode): array {
    $stmt = $pdo->prepare("
        SELECT *
        FROM payment_gateway_configs
        WHERE country_code = ?
          AND provider = 'paystack'
          AND status = 'active'
          AND is_deposit_enabled = 1
        LIMIT 1
    ");
    $stmt->execute([strtoupper($countryCode)]);
    $config = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$config) {
        ld_json(['success' => false, 'message' => 'Paystack is not enabled for this country.'], 400);
    }

    return $config;
}

function ld_reference(int $userId): string {
    return 'LDDEP_' . $userId . '_' . time() . '_' . bin2hex(random_bytes(4));
}

function ld_to_minor_units(float $amount): int {
    return (int)round($amount * 100);
}

function ld_from_minor_units(int $amountMinor): float {
    return round($amountMinor / 100, 2);
}

function ld_insert_wallet_transaction(PDO $pdo, int $userId, float $amount, string $currency, string $reference, string $description): void {
    $cols = ld_columns($pdo, 'wallet_transactions');

    if (!$cols) {
        throw new RuntimeException('wallet_transactions table/columns not found.');
    }

    $required = ['user_id', 'wallet_type', 'amount', 'transaction_type', 'txn_ref', 'description'];
    foreach ($required as $col) {
        if (!in_array($col, $cols, true)) {
            throw new RuntimeException("wallet_transactions.$col column not found.");
        }
    }

    $candidate = [
        'user_id' => $userId,
        'wallet_type' => 'play_balance',
        'amount' => $amount,
        'currency_code' => $currency,
        'transaction_type' => 'deposit',
        'txn_ref' => $reference,
        'description' => $description,
        'reference' => $reference,
        'transaction_ref' => $reference,
        'gateway_reference' => $reference,
        'provider' => 'paystack',
        'created_at' => date('Y-m-d H:i:s'),
        'updated_at' => date('Y-m-d H:i:s'),
    ];

    $data = [];
    foreach ($candidate as $col => $value) {
        if (in_array($col, $cols, true)) {
            $data[$col] = $value;
        }
    }

    $fields = array_keys($data);
    $placeholders = array_fill(0, count($fields), '?');

    $sql = "INSERT INTO wallet_transactions (`" . implode('`,`', $fields) . "`) VALUES (" . implode(',', $placeholders) . ")";
    $stmt = $pdo->prepare($sql);
    $stmt->execute(array_values($data));
}

function ld_credit_play_balance(PDO $pdo, int $userId, float $amount): void {
    $cols = ld_columns($pdo, 'users');

    if (!in_array('play_balance', $cols, true)) {
        throw new RuntimeException('users.play_balance column not found.');
    }

    $stmt = $pdo->prepare("UPDATE users SET play_balance = play_balance + ? WHERE id = ?");
    $stmt->execute([$amount, $userId]);
}

function ld_process_successful_deposit(PDO $pdo, string $reference, array $verifyResponse): array {
    $data = $verifyResponse['data'] ?? [];

    if (($data['status'] ?? '') !== 'success') {
        return [
            'success' => false,
            'status' => $data['status'] ?? 'unknown',
            'message' => 'Transaction is not successful yet.',
        ];
    }

    $paystackAmountMinor = (int)($data['amount'] ?? 0);
    $paystackCurrency = strtoupper((string)($data['currency'] ?? ''));
    $paystackFeesMinor = (int)($data['fees'] ?? 0);

    $pdo->beginTransaction();

    try {
        $stmt = $pdo->prepare("
            SELECT *
            FROM payment_deposit_intents
            WHERE internal_reference = ? OR gateway_reference = ?
            LIMIT 1
            FOR UPDATE
        ");
        $stmt->execute([$reference, $reference]);
        $intent = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$intent) {
            throw new RuntimeException('Deposit intent not found.');
        }

        if ((int)$intent['credited_wallet'] === 1 && $intent['status'] === 'paid') {
            $pdo->commit();
            return [
                'success' => true,
                'status' => 'paid',
                'already_credited' => true,
                'amount' => $intent['amount'],
                'currency_code' => $intent['currency_code'],
                'reference' => $intent['internal_reference'],
            ];
        }

        if ((int)$intent['amount_minor'] !== $paystackAmountMinor) {
            throw new RuntimeException('Amount mismatch.');
        }

        if (strtoupper($intent['currency_code']) !== $paystackCurrency) {
            throw new RuntimeException('Currency mismatch.');
        }

        $userId = (int)$intent['user_id'];
        $amount = (float)$intent['amount'];
        $fee = ld_from_minor_units($paystackFeesMinor);
        $net = max(0, round($amount - $fee, 2));
        $currency = strtoupper($intent['currency_code']);

        ld_credit_play_balance($pdo, $userId, $amount);

        ld_insert_wallet_transaction(
            $pdo,
            $userId,
            $amount,
            $currency,
            $intent['internal_reference'],
            'Paystack deposit credited: ' . $intent['internal_reference']
        );

        $update = $pdo->prepare("
            UPDATE payment_deposit_intents
            SET status = 'paid',
                credited_wallet = 1,
                gateway_fee = ?,
                wallet_credit_amount = ?,
                net_settlement_amount = ?,
                raw_response = ?,
                paid_at = NOW()
            WHERE id = ?
        ");
        $update->execute([
            $fee,
            $amount,
            $net,
            json_encode($verifyResponse, JSON_UNESCAPED_SLASHES),
            $intent['id'],
        ]);

        $pdo->commit();

        return [
            'success' => true,
            'status' => 'paid',
            'already_credited' => false,
            'amount' => number_format($amount, 2, '.', ''),
            'currency_code' => $currency,
            'gateway_fee_absorbed_by_benaplay' => number_format($fee, 2, '.', ''),
            'wallet_credit_amount' => number_format($amount, 2, '.', ''),
            'reference' => $intent['internal_reference'],
        ];
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        throw $e;
    }
}
