<?php
//Developer: core tech web designTechnology
//Whatsapp: +2349162671360
//Telegram: @jeditech


ob_start();
header('Content-Type: application/json; charset=utf-8');

require_once __DIR__ . '/../../include/config.php';
require_once __DIR__ . '/../../include/otp_functions.php';
require_once __DIR__ . '/../../include/smspva_functions.php';


// Ensure no stray output breaks JSON
if (!function_exists('json_clean_response')) {
    function json_clean_response(array $data, int $code = 200): void {
        ob_clean();
        http_response_code($code);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($data);
        exit;
    }
}

// ── Input validation ─────────────────────────────────────────────────────────

if (empty($_GET['server'])) {
    json_clean_response(['status' => '500', 'message' => 'Invalid Server']);
}
if (empty($_GET['service'])) {
    json_clean_response(['status' => '500', 'message' => 'Invalid Service']);
}
if (empty($_GET['token'])) {
    json_clean_response(['status' => '500', 'message' => 'Token Blank']);
}

$token      = trim($_GET['token']);
$server_id  = trim($_GET['server']);
$service_id = trim($_GET['service']);   // smscodes: GUID | smspva: opt20-style code
$operator   = trim($_GET['operator'] ?? ''); // optional: smspva operator (REQUIRED for Server 1)

// ── Auth ─────────────────────────────────────────────────────────────────────

$user_id = otp_verify_user($token, $pdo);
if ($user_id === false) {
    json_clean_response(['status' => '500', 'message' => 'Invalid session. Please login again.']);
}

// ── Load server + API config ──────────────────────────────────────────────────

$server_data = otp_get_server($server_id, $pdo);
if (!$server_data) {
    json_clean_response(['status' => '500', 'message' => 'Server not found.']);
}

$api_data = otp_get_api($server_data['api_id'], $pdo);
if (!$api_data) {
    json_clean_response(['status' => '500', 'message' => 'API configuration not found.']);
}

$provider = $api_data['api_type'] ?? 'smscodes'; // 'smscodes' | 'smspva'

// ── Fetch service from DB ─────────────────────────────────────────────────────

try {
    $svc_stmt = $pdo->prepare(
        "SELECT * FROM service
          WHERE service_id = :service_id
            AND server_id  = :server_id
          LIMIT 1"
    );
    $svc_stmt->execute([':service_id' => $service_id, ':server_id' => $server_id]);
    $service_data = $svc_stmt->fetch(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
    json_clean_response(['status' => '500', 'message' => 'Database error.']);
}

if (!$service_data) {
    json_clean_response(['status' => '500', 'message' => 'Service not found.']);
}

// ── Determine final purchase price ────────────────────────────────────────────

$service_price = (float)$service_data['service_price'];

// smscodes: fetch live price (fast ~2s, needed for accuracy)
if ($provider === 'smscodes') {
    $usd_price = otp_fetch_live_price(
        $api_data['api_key'],
        $api_data['api_url'],
        $server_data['server_code'],
        $service_id
    );

    if ($usd_price !== false && $usd_price > 0) {
        $service_price = otp_convert_price($usd_price, $pdo);

        try {
            $pdo->prepare("UPDATE service SET service_price = :p WHERE id = :id")
                ->execute([':p' => (string)$service_price, ':id' => $service_data['id']]);
        } catch (PDOException $e) { /* non-fatal */ }
    }
}

// smspva: price depends on which operator will fulfil the order.
// Use the operator-specific cached NGN price from smspva_service_cache.
// This is the price the user was shown in the UI and the price we charge.
// If no operator was passed (or cache miss), fall back to DB service_price.
if ($provider === 'smspva') {
    $cached_ngn = smspva_operator_ngn_price(
        $service_id,
        $server_data['server_code'],
        $operator,
        $pdo
    );
    if ($cached_ngn > 0) {
        $service_price = $cached_ngn;
    }
    // If $cached_ngn is 0 (cache empty), $service_price keeps the DB value — safe fallback.
}

// Apply per-user custom price override (works for both providers)
$service_price = otp_custom_price($user_id, $service_id, $server_id, $service_price, $pdo);

// ── Check user balance ────────────────────────────────────────────────────────

try {
    $bal_stmt = $pdo->prepare("SELECT main_balance FROM users WHERE id = :uid AND status = '1' LIMIT 1");
    $bal_stmt->execute([':uid' => $user_id]);
    $user_row = $bal_stmt->fetch(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
    json_clean_response(['status' => '500', 'message' => 'Balance check error.']);
}

if (!$user_row) {
    json_clean_response(['status' => '500', 'message' => 'User not found.']);
}

if ((float)$user_row['main_balance'] < $service_price) {
    json_clean_response(['status' => '500', 'message' => 'Insufficient Balance. Please fund your wallet.']);
}

// ══════════════════════════════════════════════════════════════════
// PROVIDER: smspva.com  (Server1)
// ══════════════════════════════════════════════════════════════════
if ($provider === 'smspva') {

    $result = smspva_buy_number(
        $api_data['api_url'],
        $api_data['api_key'],
        $server_data['server_code'],
        $service_id,
        $operator
    );

    if (!$result['success']) {
        $http_code = (int)($result['http_code'] ?? 0);
        $msg = smspva_buy_error_message($http_code, $result['error'] ?? '');
        json_clean_response(['status' => '500', 'message' => $msg]);
    }

    // ── Price-mismatch guard ──────────────────────────────────────────────────
    // smspva returns the actual price it will charge us in $result['price'] (USD).
    // If the actual USD cost is significantly higher than the cached operator price
    // we showed the user, it means smspva switched to a more expensive operator.
    // In that case we abort BEFORE deducting the user's wallet — no loss for us.
    $actual_usd   = (float)$result['price'];
    $expected_usd = smspva_operator_usd_price($service_id, $server_data['server_code'], $operator, $pdo);

    // Allow 20% tolerance to absorb minor fluctuations (e.g. rounding differences).
    // If expected is 0 (cache miss), skip the guard.
    if ($expected_usd > 0 && $actual_usd > 0 && $actual_usd > $expected_usd * 1.20) {
        // smspva already reserved the number — we have to live with it, but we
        // will charge the user the actual cost (converted to NGN) rather than
        // the cached price, so we don't lose money. We recompute service_price
        // from the real USD amount and apply custom-price override on top.
        $adjusted_ngn  = smspva_convert_price($actual_usd, $pdo);
        $adjusted_ngn  = otp_custom_price($user_id, $service_id, $server_id, $adjusted_ngn, $pdo);
        $service_price = $adjusted_ngn;

        // Re-check balance with new price before proceeding
        $bal_stmt2 = $pdo->prepare("SELECT main_balance FROM users WHERE id = :uid AND status = '1' LIMIT 1");
        $bal_stmt2->execute([':uid' => $user_id]);
        $user_row2 = $bal_stmt2->fetch(PDO::FETCH_ASSOC);
        if (!$user_row2 || (float)$user_row2['main_balance'] < $service_price) {
            // Insufficient balance for the real cost — cannot complete purchase
            json_clean_response([
                'status'  => '500',
                'message' => 'Purchase aborted: operator price changed and balance is insufficient. Please fund your wallet and try again.',
            ]);
        }
    }
    // ── End price-mismatch guard ─────────────────────────────────────────────

    $phone_number        = smspva_format_number($result['number'], $server_data['server_code']);
    $provider_order_id   = (string)$result['order_id']; // smspva integer orderId
    $used_operator       = $result['operator'];

    // ── Deduct balance + record order ────────────────────────────────────────

    $order_id = otp_random_string(20);
    $buy_time = date('Y-m-d H:i:s');

    try {
        $pdo->beginTransaction();

        $lock_stmt = $pdo->prepare("SELECT main_balance FROM users WHERE id = :uid FOR UPDATE");
        $lock_stmt->execute([':uid' => $user_id]);
        $locked = $lock_stmt->fetch(PDO::FETCH_ASSOC);

        if ((float)$locked['main_balance'] < $service_price) {
            $pdo->rollBack();
            json_clean_response(['status' => '500', 'message' => 'Insufficient Balance.']);
        }

        $final_balance = (float)$locked['main_balance'] - $service_price;

        $upd = $pdo->prepare("UPDATE users SET main_balance = :balance WHERE id = :uid");
        $upd->execute([':balance' => $final_balance, ':uid' => $user_id]);

        if ($upd->rowCount() === 0) {
            throw new RuntimeException('Failed to deduct balance.');
        }

        // number_id stores smspva's orderId (used to poll SMS)
        $ins = $pdo->prepare(
            "INSERT INTO active_number
                (user_id, number_id, number, server_id, service_id, order_id,
                 buy_time, status, sms_text, service_price, service_name,
                 active_status, provider, operator)
             VALUES
                (:user_id, :number_id, :number, :server_id, :service_id, :order_id,
                 :buy_time, '2', '', :service_price, :service_name,
                 '2', 'smspva', :operator)"
        );
        $ins->execute([
            ':user_id'       => $user_id,
            ':number_id'     => $provider_order_id,
            ':number'        => $phone_number,
            ':server_id'     => $server_id,
            ':service_id'    => $service_id,
            ':order_id'      => $order_id,
            ':buy_time'      => $buy_time,
            ':service_price' => (string)$service_price,
            ':service_name'  => $service_data['service_name'],
            ':operator'      => $used_operator,
        ]);

        $pdo->commit();

    } catch (Exception $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        json_clean_response(['status' => '500', 'message' => $e->getMessage()]);
    }

    json_clean_response([
        'status'  => '200',
        'message' => 'Number Purchased Successfully',
        'res'     => 'ACCESS_NUMBER:' . $order_id . ':' . $phone_number,
    ]);
}

// ══════════════════════════════════════════════════════════════════
// PROVIDER: smscodes.io  (Server2) — original logic preserved exactly
// ══════════════════════════════════════════════════════════════════

$provider_url = otp_build_url($api_data['api_url'], 'GetServiceNumber', $api_data['api_key'], [
    'iso'  => $server_data['server_code'],
    'serv' => $service_id,
]);

$raw_response = otp_curl_get($provider_url, 30);
$api_response = json_decode($raw_response, true);

if (
    !is_array($api_response) ||
    !isset($api_response['Status']) ||
    $api_response['Status'] !== 'Success' ||
    empty($api_response['Number']) ||
    empty($api_response['SecurityId'])
) {
    $error_msg = 'Could not get a number. ';
    if (is_array($api_response) && isset($api_response['Error']) && $api_response['Error'] !== 'Non') {
        $error_msg .= $api_response['Error'];
    } elseif (is_array($api_response) && isset($api_response['Message'])) {
        $error_msg .= $api_response['Message'];
    } elseif (strpos($raw_response, 'CURL_ERROR') === 0) {
        $error_msg .= 'API connection failed. Please try again.';
    } elseif (strpos($raw_response, 'API calls quota exceeded') !== false) {
        $error_msg .= 'Too many requests. Please wait a moment and try again.';
    } else {
        $error_msg .= 'Could not get a number. Please try again.';
    }
    json_clean_response(['status' => '500', 'message' => $error_msg]);
}

$phone_number       = $api_response['Number'];
$provider_number_id = $api_response['SecurityId'];

$order_id = otp_random_string(20);
$buy_time = date('Y-m-d H:i:s');

try {
    $pdo->beginTransaction();

    $lock_stmt = $pdo->prepare("SELECT main_balance FROM users WHERE id = :uid FOR UPDATE");
    $lock_stmt->execute([':uid' => $user_id]);
    $locked = $lock_stmt->fetch(PDO::FETCH_ASSOC);

    if ((float)$locked['main_balance'] < $service_price) {
        $pdo->rollBack();
        json_clean_response(['status' => '500', 'message' => 'Insufficient Balance.']);
    }

    $final_balance = (float)$locked['main_balance'] - $service_price;

    $upd = $pdo->prepare("UPDATE users SET main_balance = :balance WHERE id = :uid");
    $upd->execute([':balance' => $final_balance, ':uid' => $user_id]);

    if ($upd->rowCount() === 0) {
        throw new RuntimeException('Failed to deduct balance.');
    }

    $ins = $pdo->prepare(
        "INSERT INTO active_number
            (user_id, number_id, number, server_id, service_id, order_id,
             buy_time, status, sms_text, service_price, service_name,
             active_status, provider, operator)
         VALUES
            (:user_id, :number_id, :number, :server_id, :service_id, :order_id,
             :buy_time, '2', '', :service_price, :service_name,
             '2', 'smscodes', '')"
    );
    $ins->execute([
        ':user_id'       => $user_id,
        ':number_id'     => $provider_number_id,
        ':number'        => $phone_number,
        ':server_id'     => $server_id,
        ':service_id'    => $service_id,
        ':order_id'      => $order_id,
        ':buy_time'      => $buy_time,
        ':service_price' => (string)$service_price,
        ':service_name'  => $service_data['service_name'],
    ]);

    $pdo->commit();

} catch (Exception $e) {
    if ($pdo->inTransaction()) $pdo->rollBack();
    json_clean_response(['status' => '500', 'message' => $e->getMessage()]);
}

json_clean_response([
    'status'  => '200',
    'message' => 'Number Purchased Successfully',
    'res'     => 'ACCESS_NUMBER:' . $order_id . ':' . $phone_number,
]);