<?php

error_reporting(0);
ini_set('display_errors', 0);
header('X-Robots-Tag: noindex, nofollow');
ini_set('max_execution_time', 600);
ini_set('memory_limit', '2G');
date_default_timezone_set('UTC');
$request_start_time = time(); // 🕒 LOGIC: Request Start Time

// ---------------------------------------------------------
// 1. DATABASE CONNECTION (Forced to sn4check_db)
// ---------------------------------------------------------
$connect = @new mysqli("localhost", "sn4check_db", "tYp3APfN33i6xwKi", "sn4check_db");
if ($connect->connect_error) {
    header('Content-Type: application/json');
    die(json_encode(['error' => 'Database Connection Failed']));
}

$phpApiRequestStartMs = microtime(true);
$clientApiLoggerPath = dirname(__DIR__, 2) . '/includes/client_api_logger.php';
if (is_file($clientApiLoggerPath)) {
    require_once $clientApiLoggerPath;
    client_api_logs_ensure_table($connect);
}
$phpApiInputPath = dirname(__DIR__, 2) . '/includes/php_api_input.php';
if (is_file($phpApiInputPath)) {
    require_once $phpApiInputPath;
}

function phpApiWriteClientLog($connect, array $entry) {
    global $phpApiRequestStartMs;
    if (!function_exists('client_api_log_write')) {
        return;
    }
    if (!isset($entry['duration_ms'])) {
        $entry['duration_ms'] = (int) round((microtime(true) - $phpApiRequestStartMs) * 1000);
    }
    if (!isset($entry['ip_address'])) {
        $entry['ip_address'] = getClientIP();
    }
    if (!isset($entry['api_source'])) {
        $entry['api_source'] = 'php';
    }
    if (!isset($entry['request_summary']) || $entry['request_summary'] === '') {
        $phpSnap = client_api_logs_get_php_snapshot();
        if ($phpSnap !== '') {
            $entry['request_summary'] = $phpSnap;
        }
    }
    if ((!isset($entry['response_summary']) || $entry['response_summary'] === '') && !empty($GLOBALS['_client_api_php_response'])) {
        $entry['response_summary'] = (string) $GLOBALS['_client_api_php_response'];
    }
    client_api_log_write($connect, $entry);
}

function phpApiSetLogResponse($payload) {
    if (is_string($payload)) {
        $GLOBALS['_client_api_php_response'] = $payload;
        return;
    }
    $GLOBALS['_client_api_php_response'] = json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}

// ---------------------------------------------------------
// LOGGING FUNCTION
// ---------------------------------------------------------
function logEvent($type, $message) {
    $logFile = __DIR__ . '/dhru_api_debug.log';
    $timestamp = date('Y-m-d H:i:s');
    $logLine = "[$timestamp] [$type] $message\n";
    @file_put_contents($logFile, $logLine, FILE_APPEND | LOCK_EX);
}





// ---------------------------------------------------------
// 2. HELPER FUNCTIONS
// ---------------------------------------------------------

function getClientIP() {
    $ipKeys = ['HTTP_CF_CONNECTING_IP', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 
               'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR'];
    foreach ($ipKeys as $key) {
        if (!empty($_SERVER[$key])) {
            $ip = $_SERVER[$key];
            if ($key === 'HTTP_X_FORWARDED_FOR') $ip = explode(',', $ip)[0];
            $ip = trim($ip);
            if (filter_var($ip, FILTER_VALIDATE_IP)) return $ip;
        }
    }
    return $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
}

function isIpAllowed($allowedIpsStr, $clientIp) {
    if (empty($allowedIpsStr)) return true;
    $allowedIps = array_map('trim', explode(',', $allowedIpsStr));
    $allowedIps = array_filter($allowedIps);
    if (empty($allowedIps)) return true;
    return in_array($clientIp, $allowedIps);
}

function loadEndpointsJson() {
    static $config = null;
    if ($config === null) {
         $file = '/www/wwwroot/icheckimei.info/amira2026/admin/configs/endpoints.json';
        if (!file_exists($file)) $file = __DIR__ . '/endpoints.json';
        $config = file_exists($file) ? json_decode(file_get_contents($file), true) : ['services' => []];
    }
    return $config;
}

function getEndpointConfig($service) {
    $config = loadEndpointsJson();
    return $config['services'][strval($service)] ?? null;
}

function checkWordMatch($response, $words) {
    if (empty($words) || empty($response)) return false;
    if (strpos($words, ' & ') !== false) {
        $list = array_filter(array_map('trim', explode(' & ', $words)));
        foreach ($list as $w) {
            if ($w !== '' && stripos($response, $w) === false) return false;
        }
        return !empty($list);
    }
    if (strpos($words, '||') !== false || strpos($words, '|') !== false) {
        $words = preg_replace('/\|+/', '||', $words);
        $list = array_filter(array_map('trim', explode('||', $words)));
        foreach ($list as $w) {
            if ($w !== '' && stripos($response, $w) !== false) return true;
        }
        return false;
    }
    return stripos($response, $words) !== false;
}

function validateEndpointResult($service, $response, $imei, $linkNumber = '1') {
    $endpoint = getEndpointConfig($service);
    if (!$endpoint) {
        logEvent("VALIDATE-FAIL", "Service $service | No endpoint config");
        return false;
    }
    
    // 🛡️ Reject empty responses
    if (empty($response) || strlen(trim($response)) < 10) {
        logEvent("VALIDATE-FAIL", "Service $service | IMEI $imei | Empty/short response: " . strlen($response) . " bytes");
        return false;
    }
    
    // 🛡️ Reject common error pages (404, 500, etc.)
    $errorPatterns = [
        '404 Not Found',
        '403 Forbidden', 
        '500 Internal Server Error',
        '502 Bad Gateway',
        '503 Service Unavailable',
        'No response from server',
        'API did not return a valid response',
        'No response from check service',
        'Invalid response from check service',
        'Page not found',
        'page you visited does not exist',
        'Access Denied',
        'aaPanel',
        'nginx error',
        'Apache error',
        'cPanel'
    ];
    foreach ($errorPatterns as $pattern) {
        if (stripos($response, $pattern) !== false) {
            logEvent("VALIDATE-FAIL", "Service $service | IMEI $imei | Error pattern: $pattern");
            return false;
        }
    }
    
    $successWord = ($linkNumber === '2' && isset($endpoint['link2'])) 
        ? ($endpoint['link2']['success_word'] ?? $endpoint['success_word'] ?? '')
        : ($endpoint['success_word'] ?? '');
    $rejectWord = ($linkNumber === '2' && isset($endpoint['link2']))
        ? ($endpoint['link2']['reject_word'] ?? $endpoint['reject_word'] ?? '')
        : ($endpoint['reject_word'] ?? '');
    $rejectIfNoMatch = ($linkNumber === '2' && isset($endpoint['link2']))
        ? ($endpoint['link2']['reject_if_no_match'] ?? $endpoint['reject_if_no_match'] ?? false)
        : ($endpoint['reject_if_no_match'] ?? false);

    // 🟢 Step 1: Check success_word FIRST - if set and found → ACCEPT immediately
    if (!empty($successWord) && checkWordMatch($response, $successWord)) {
        return true; // ✅ Success word found - ACCEPT
    }
    
    // 🔴 Step 2: Check reject_word - if set and found → REJECT
    if (!empty($rejectWord) && checkWordMatch($response, $rejectWord)) {
        logEvent("VALIDATE-FAIL", "Service $service | IMEI $imei | Reject word matched: $rejectWord");
        return false; // ❌ Reject word found - REJECT
    }
    
    // 🟡 Step 3: Neither success nor reject word matched
    // If success_word was set but not found → will try link2 (return false to trigger fallback)
    if (!empty($successWord)) {
        logEvent("VALIDATE-FAIL", "Service $service | IMEI $imei | Success word NOT found: $successWord (will try link2 if available)");
        return false; // ❌ Success word not found - try link2
    }
    
    // 🟠 Step 4: No success_word set - check reject_if_no_match
    if ($rejectIfNoMatch) {
        logEvent("VALIDATE-FAIL", "Service $service | IMEI $imei | No success_word and reject_if_no_match=true");
        return false; // ❌ reject_if_no_match is true - REJECT
    }
    
    // ✅ No success_word, no reject_word matched, reject_if_no_match=false - ACCEPT
    return true;
}

function CURL($url, $timeout = 60, $retries = 2) {
    $lastError = '';
    for ($attempt = 0; $attempt <= $retries; $attempt++) {
        if ($attempt > 0) {
            usleep(300000 * $attempt); // 300ms, 600ms delay between retries
        }
        
        $curl = curl_init($url);
        curl_setopt_array($curl, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_CONNECTTIMEOUT => min(25, $timeout),
            CURLOPT_TIMEOUT => $timeout,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
            CURLOPT_ENCODING => '',
            CURLOPT_TCP_KEEPALIVE => 1,
            CURLOPT_TCP_KEEPIDLE => 120,
            CURLOPT_TCP_KEEPINTVL => 60,
            CURLOPT_TCP_NODELAY => 1,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
            CURLOPT_FRESH_CONNECT => ($attempt > 0), // Fresh connection on retry
            CURLOPT_FORBID_REUSE => false,
            CURLOPT_DNS_CACHE_TIMEOUT => 300,
            CURLOPT_BUFFERSIZE => 131072
        ]);
        $res = curl_exec($curl);
        $errno = curl_errno($curl);
        $error = curl_error($curl);
        curl_close($curl);
        
        if ($errno === 0 && !empty($res)) {
            return $res;
        }
        $lastError = "cURL Error $errno: $error";
    }
    logEvent("CURL-FAIL", "URL: " . substr($url, 0, 100) . "... | $lastError after $retries retries");
    return '';
}

function multiCURL($requests, $connectTimeout = 15, $timeout = 45) {
    if (empty($requests)) return [];
    
    $totalRequests = count($requests);
    $maxConcurrent = 20; // 🚀 Keep 20 requests active at all times (rolling window)
    
    logEvent("ROLLING-START", "Processing $totalRequests requests with rolling window of $maxConcurrent");
    
    $allResponses = [];
    $startTime = microtime(true);
    
    $mh = curl_multi_init();
    @curl_multi_setopt($mh, CURLMOPT_MAXCONNECTS, $maxConcurrent);
    
    $handles = [];      // key => curl handle
    $handleToKey = [];  // resource id => key (for mapping completed handles)
    $pending = $requests; // requests not yet started
    $totalSuccess = 0;
    $totalFailed = 0;
    $failedRequests = []; // for retry
    
    // Helper to create a curl handle
    $createHandle = function($key, $url) use ($connectTimeout, $timeout) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
            CURLOPT_CONNECTTIMEOUT => $connectTimeout,
            CURLOPT_TIMEOUT => $timeout,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            CURLOPT_ENCODING => '',
            CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
            CURLOPT_TCP_KEEPALIVE => 1,
            CURLOPT_TCP_NODELAY => 1,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_PRIVATE => $key, // Store key in handle
        ]);
        return $ch;
    };
    
    // 🔥 Start initial batch (up to maxConcurrent)
    $started = 0;
    foreach ($pending as $key => $url) {
        if ($started >= $maxConcurrent) break;
        if (empty($url)) {
            $allResponses[$key] = '';
            unset($pending[$key]);
            continue;
        }
        $ch = $createHandle($key, $url);
        $handles[$key] = $ch;
        $handleToKey[(int)$ch] = $key;
        curl_multi_add_handle($mh, $ch);
        unset($pending[$key]);
        $started++;
    }
    
    logEvent("ROLLING-INITIAL", "Started $started initial requests, " . count($pending) . " pending");
    
    // 🔄 ROLLING WINDOW - Process responses and add new requests as slots free up
    $active = null;
    do {
        $mrc = curl_multi_exec($mh, $active);
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);
    
    while ($active || !empty($pending)) {
        // Wait for activity
        if (curl_multi_select($mh, 0.5) == -1) {
            usleep(10000);
        }
        
        // Execute
        do {
            $mrc = curl_multi_exec($mh, $active);
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        
        // 🔍 Check for completed transfers
        while ($info = curl_multi_info_read($mh)) {
            $ch = $info['handle'];
            $key = curl_getinfo($ch, CURLINFO_PRIVATE);
            
            $errno = curl_errno($ch);
            $error = curl_error($ch);
            $content = curl_multi_getcontent($ch);
            
            if ($errno !== 0 || empty($content)) {
                $totalFailed++;
                $allResponses[$key] = '';
                $failedRequests[$key] = $requests[$key]; // Save for retry
                if ($errno !== 0) {
                    logEvent("ROLLING-ERR", "Key $key | Error $errno: $error");
                }
            } else {
                $totalSuccess++;
                $allResponses[$key] = $content;
            }
            
            // Remove completed handle
            curl_multi_remove_handle($mh, $ch);
            curl_close($ch);
            unset($handles[$key]);
            
            // 🔥 ADD NEW REQUEST to fill the slot
            if (!empty($pending)) {
                $nextKey = array_key_first($pending);
                $nextUrl = $pending[$nextKey];
                unset($pending[$nextKey]);
                
                if (!empty($nextUrl)) {
                    $newCh = $createHandle($nextKey, $nextUrl);
                    $handles[$nextKey] = $newCh;
                    curl_multi_add_handle($mh, $newCh);
                } else {
                    $allResponses[$nextKey] = '';
                }
            }
        }
        
        // Safety: break if no active handles and nothing pending
        if ($active == 0 && empty($pending)) break;
    }
    
    curl_multi_close($mh);
    
    $parallelTime = round(microtime(true) - $startTime, 2);
    logEvent("ROLLING-DONE", "Completed in {$parallelTime}s | Success: $totalSuccess | Failed: $totalFailed");
    
    // 🔄 RETRY failed requests (sequentially with delays)
    if (!empty($failedRequests)) {
        logEvent("RETRY-START", "Retrying " . count($failedRequests) . " failed requests");
        foreach ($failedRequests as $key => $url) {
            usleep(300000); // 300ms delay
            $retryResult = CURL($url, $timeout, 1);
            if (!empty($retryResult)) {
                $allResponses[$key] = $retryResult;
                $totalSuccess++;
                $totalFailed--;
                logEvent("RETRY-OK", "Key $key recovered");
            }
        }
        logEvent("RETRY-DONE", "Final: Success: $totalSuccess | Failed: $totalFailed");
    }
    
    return $allResponses;
}

function callEndpointWithFallback($service, $imei) {
    $endpoint = getEndpointConfig($service);
    if (!$endpoint) return ['success' => false, 'response' => ''];
    
    $timeout1 = (int)($endpoint['timeout'] ?? 30);
    $hasLink2 = isset($endpoint['link2']) && is_array($endpoint['link2']) && !empty($endpoint['link2']['url']);
    $timeout2 = $hasLink2 ? (int)($endpoint['link2']['timeout'] ?? $timeout1) : $timeout1;
    $link2Mode = $endpoint['link2_mode'] ?? 'fallback';
    
    // Mode "only" - skip link1, use link2 directly
    if ($hasLink2 && $link2Mode === 'only') {
        $res = CURL(str_replace('{imei}', $imei, $endpoint['link2']['url']), $timeout2);
        return ['success' => validateEndpointResult($service, $res, $imei, '2'), 'response' => $res];
    }
    
    // Try link1 first
    $res1 = CURL(str_replace('{imei}', $imei, $endpoint['url']), $timeout1);
    $link1Valid = validateEndpointResult($service, $res1, $imei, '1');
    
    // If link1 succeeded, return it
    if ($link1Valid) {
        return ['success' => true, 'response' => $res1];
    }
    
    // Link1 failed - try link2 if available (fallback mode)
    if ($hasLink2) {
        $res2 = CURL(str_replace('{imei}', $imei, $endpoint['link2']['url']), $timeout2);
        $link2Valid = validateEndpointResult($service, $res2, $imei, '2');
        return ['success' => $link2Valid, 'response' => $res2];
    }
    
    // No link2 available, return link1's failed result
    return ['success' => false, 'response' => $res1];
}

function normalizeResult($result, $imei)
{
    if ($result === '' || $result === null) {
        return json_encode(['error' => 'Wrong IMEI or Service Offline!']);
    }

    $result = trim($result);

    /* 1️⃣ Strong JSON guard */
    $decoded = json_decode($result, true);
    if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
        return json_encode(
            $decoded,
            JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
        );
    }

    /* 2️⃣ HTML / TEXT parsing */
    $text = preg_replace('/<br\s*\/?>/i', "\n", $result);
    $text = strip_tags($text);
    $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    $text = str_replace(["\xc2\xa0", "\xa0"], ' ', $text);

    $lines = array_values(array_filter(array_map(
        'trim',
        preg_split("/\r\n|\r|\n/", $text)
    )));

    $data = [];
    $currentSection = null;

    foreach ($lines as $line) {

        /* ---- SECTION HEADERS (EXPLICIT ONLY) ---- */
        if (preg_match('/^Repair Warnings:$/i', $line)) {
            $currentSection = 'Repair Options';
            $data[$currentSection] = [];
            continue;
        }

        if (preg_match('/^Repair Options:$/i', $line)) {
            $currentSection = 'Repair Options';
            $data[$currentSection] = [];
            continue;
        }

        if (preg_match('/^No Replacement History$/i', $line)) {
            $data['Replacement'] = ['No Replacement History'];
            $currentSection = null;
            continue;
        }

        /* ---- INSIDE A SECTION ---- */
        if ($currentSection !== null) {
            $data[$currentSection][] = $line;
            continue;
        }

        /* ---- NORMAL key : value ---- */
        if (strpos($line, ':') !== false) {
            [$k, $v] = explode(':', $line, 2);
            $key = trim($k);
            $val = trim($v);

            if ($key !== '' && $val !== '') {
                if (isset($data[$key])) {
                    if (!is_array($data[$key])) {
                        $data[$key] = [$data[$key]];
                    }
                    $data[$key][] = $val;
                } else {
                    $data[$key] = $val;
                }
            }
            continue;
        }

        /* ---- STANDALONE LINE (fallback) ---- */
        $data[] = $line;
    }

    return json_encode(
        $data ?: ['error' => 'Wrong IMEI or Service Offline!'],
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );
}
function formatRawToText($raw) {
    $decoded = json_decode($raw, true);
    if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
        return $raw;
    }
    $text = "";
    foreach ($decoded as $k => $v) {
        if (is_array($v)) $v = json_encode($v, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        elseif (is_bool($v)) $v = $v ? "true" : "false";
        $text .= "$k: $v<br>";
    }
    return $text;
}

/**
 * For json=1: flat reply like legacy json=1; profit_warranty_blocks only if "Profits Type Name …" sections exist.
 */
function parseCoverageResultToGroupedReply($result) {
    if ($result === '' || $result === null) {
        return ['error' => 'Wrong IMEI or Service Offline!'];
    }
    $result = trim((string) $result);

    $decoded = json_decode($result, true);
    if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
        return $decoded;
    }

    $text = preg_replace('/<br\s*\/?>/i', "\n", $result);
    $text = strip_tags($text);
    $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    $text = str_replace(["\xc2\xa0", "\xa0"], ' ', $text);

    $lines = array_values(array_filter(array_map(
        'trim',
        preg_split("/\r\n|\r|\n/", $text)
    )));

    $device = [];
    $profit_blocks = [];
    $currentSection = null;

    foreach ($lines as $line) {
        if (preg_match('/^Repair Warnings:$/i', $line)) {
            $currentSection = 'Repair Options';
            $device[$currentSection] = [];
            continue;
        }
        if (preg_match('/^Repair Options:$/i', $line)) {
            $currentSection = 'Repair Options';
            $device[$currentSection] = [];
            continue;
        }
        if (preg_match('/^No Replacement History$/i', $line)) {
            $device['Replacement'] = ['No Replacement History'];
            $currentSection = null;
            continue;
        }

        if ($currentSection !== null) {
            $device[$currentSection][] = $line;
            continue;
        }

        if (strpos($line, ':') !== false) {
            [$k, $v] = explode(':', $line, 2);
            $key = trim($k);
            $val = trim($v);
            if ($key === '' || $val === '') {
                continue;
            }

            if (strcasecmp($key, 'Profits Type Name') === 0) {
                $profit_blocks[] = ['Profits Type Name' => $val];
                continue;
            }

            if (count($profit_blocks) > 0) {
                $idx = count($profit_blocks) - 1;
                if (isset($profit_blocks[$idx][$key])) {
                    if (!is_array($profit_blocks[$idx][$key])) {
                        $profit_blocks[$idx][$key] = [$profit_blocks[$idx][$key]];
                    }
                    $profit_blocks[$idx][$key][] = $val;
                } else {
                    $profit_blocks[$idx][$key] = $val;
                }
                continue;
            }

            if (isset($device[$key])) {
                if (!is_array($device[$key])) {
                    $device[$key] = [$device[$key]];
                }
                $device[$key][] = $val;
            } else {
                $device[$key] = $val;
            }
            continue;
        }

        $device[] = $line;
    }

    $out = $device;
    if (!empty($profit_blocks)) {
        $out['profit_warranty_blocks'] = $profit_blocks;
    }
    return $out;
}

/** Content-Type when json=1: reply is JSON object (flat; profit_warranty_blocks only if present). */
function apiJsonModeContentType() {
    header('Content-Type: application/json; charset=utf-8');
}

/**
 * Resolved API price for a service.
 * Free (0) only when tbl_discount_imei has an explicit numeric price = 0 for this user+service.
 * Without that row, pricing is group / service_pricing / base credit — never 0 (so no unpaid order when a price exists there).
 * @return float|null null = invalid / no price; -1.0 = disabled (≥99999); 0.0 = free (explicit discount 0 only); else paid amount
 */
function getServicePrice($connect, $serviceId, $userId, $groupId) {
    $serviceId = (int) $serviceId;
    $baseRes = $connect->query("SELECT credit FROM tbl_services_imei WHERE id = $serviceId LIMIT 1");
    if (!$baseRes || !($baseRow = $baseRes->fetch_assoc())) {
        return null;
    }
    $groupId = intval($groupId);

    $discRes = $connect->query("SELECT price FROM tbl_discount_imei WHERE service_id = $serviceId AND user_id = $userId LIMIT 1");
    if ($discRes && $row = $discRes->fetch_assoc()) {
        $rawDisc = $row['price'] ?? null;
        // NULL/empty must NOT become floatval(0) — that would wrongly grant free orders.
        if ($rawDisc !== null && $rawDisc !== '') {
            $rawStr = trim((string) $rawDisc);
            if ($rawStr !== '' && is_numeric($rawStr)) {
                $p = (float) $rawStr;
                if ($p >= 99999) {
                    return -1.0;
                }
                if ($p == 0.0) {
                    return 0.0;
                }
                if ($p > 0 && $p < 99999) {
                    return $p;
                }

                return null;
            }
            if ($rawStr !== '') {
                return null;
            }
        }
        // Row exists but price NULL/empty/whitespace — fall through to group/base (same as no discount row).
    }

    $finalPrice = null;
    $cGrpRes = $connect->query("SELECT price FROM tbl_client_group_price WHERE service_id = $serviceId AND group_id = $groupId LIMIT 1");
    if ($cGrpRes && $row = $cGrpRes->fetch_assoc()) {
        $p = floatval($row['price']);
        if ($p >= 99999) {
            return -1.0;
        }
        if ($p > 0 && $p < 99999) {
            $finalPrice = $p;
        }
    }
    if ($finalPrice === null) {
        $grpRes = $connect->query("SELECT price FROM tbl_service_pricing WHERE serviceid = $serviceId AND groupid = '$groupId' LIMIT 1");
        if ($grpRes && $row = $grpRes->fetch_assoc()) {
            $p = floatval($row['price']);
            if ($p >= 99999) {
                return -1.0;
            }
            if ($p > 0 && $p < 99999) {
                $finalPrice = $p;
            }
        }
    }
    if ($finalPrice === null) {
        $bp = floatval($baseRow['credit']);
        if ($bp >= 99999) {
            return -1.0;
        }
        if ($bp > 0 && $bp < 99999) {
            $finalPrice = $bp;
        }
    }

    return $finalPrice !== null ? (float) $finalPrice : null;
}

/** Resolve price from preloaded batch maps (same rules as getServicePrice). */
function phpApiResolvePriceFromBatch($serviceId, $baseCredit, $discountPrice, $clientGroupPrice, $groupPrice) {
    if ($discountPrice !== null) {
        $rawStr = trim((string) $discountPrice);
        if ($rawStr !== '' && is_numeric($rawStr)) {
            $p = (float) $rawStr;
            if ($p >= 99999) {
                return -1.0;
            }
            if ($p == 0.0) {
                return 0.0;
            }
            if ($p > 0 && $p < 99999) {
                return $p;
            }
            return null;
        }
        if ($rawStr !== '') {
            return null;
        }
    }

    $finalPrice = null;
    if ($clientGroupPrice !== null && $clientGroupPrice !== '') {
        $p = (float) $clientGroupPrice;
        if ($p >= 99999) {
            return -1.0;
        }
        if ($p > 0 && $p < 99999) {
            $finalPrice = $p;
        }
    }
    if ($finalPrice === null && $groupPrice !== null && $groupPrice !== '') {
        $p = (float) $groupPrice;
        if ($p >= 99999) {
            return -1.0;
        }
        if ($p > 0 && $p < 99999) {
            $finalPrice = $p;
        }
    }
    if ($finalPrice === null && $baseCredit !== null && $baseCredit !== '') {
        $p = (float) $baseCredit;
        if ($p >= 99999) {
            return -1.0;
        }
        if ($p > 0 && $p < 99999) {
            $finalPrice = $p;
        }
    }

    return $finalPrice !== null ? (float) $finalPrice : null;
}

/** Batch-load prices for service list (avoids N+1 queries on slow databases). */
function phpApiBatchServicePrices($connect, $userId, $groupId, array $serviceIds) {
    $userId = (int) $userId;
    $groupId = (int) $groupId;
    $serviceIds = array_values(array_unique(array_filter(array_map('intval', $serviceIds), static function ($id) {
        return $id > 0;
    })));
    if (empty($serviceIds)) {
        return [];
    }

    $in = implode(',', $serviceIds);
    $baseCredits = [];
    $discounts = [];
    $clientGroupPrices = [];
    $groupPrices = [];

    $baseRes = $connect->query("SELECT id, credit FROM tbl_services_imei WHERE id IN ($in)");
    if ($baseRes) {
        while ($row = $baseRes->fetch_assoc()) {
            $baseCredits[(int) $row['id']] = $row['credit'];
        }
    }

    $discRes = $connect->query("SELECT service_id, price FROM tbl_discount_imei WHERE user_id = $userId AND service_id IN ($in)");
    if ($discRes) {
        while ($row = $discRes->fetch_assoc()) {
            $discounts[(int) $row['service_id']] = $row['price'];
        }
    }

    $cGrpRes = @$connect->query("SELECT service_id, price FROM tbl_client_group_price WHERE group_id = $groupId AND service_id IN ($in) AND service_type = 'imei'");
    if ($cGrpRes) {
        while ($row = $cGrpRes->fetch_assoc()) {
            $clientGroupPrices[(int) $row['service_id']] = $row['price'];
        }
    }

    $grpRes = $connect->query("SELECT serviceid, price FROM tbl_service_pricing WHERE groupid = '$groupId' AND serviceid IN ($in)");
    if ($grpRes) {
        while ($row = $grpRes->fetch_assoc()) {
            $groupPrices[(int) $row['serviceid']] = $row['price'];
        }
    }

    $out = [];
    foreach ($serviceIds as $serviceId) {
        $price = phpApiResolvePriceFromBatch(
            $serviceId,
            $baseCredits[$serviceId] ?? null,
            array_key_exists($serviceId, $discounts) ? $discounts[$serviceId] : null,
            $clientGroupPrices[$serviceId] ?? null,
            $groupPrices[$serviceId] ?? null
        );
        if ($price !== null) {
            $out[$serviceId] = $price;
        }
    }

    return $out;
}

function insertOrderAndCharge($link, $service, $id, $imei, $note, $result, $costa, $ip, $start_time, $date, $status = 4) {
    $safe_imei   = mysqli_real_escape_string($link, (string)($imei ?? ''));
    $safe_note   = mysqli_real_escape_string($link, (string)($note ?? 'Rejected'));
    $safe_result = mysqli_real_escape_string($link, (string)($result ?? ''));
    $safe_ip     = mysqli_real_escape_string($link, (string)($ip ?? '0.0.0.0'));
    $safe_date   = mysqli_real_escape_string($link, (string)($date ?? date('Y-m-d')));
    $safe_costa  = (float)($costa ?? 0); 
    $order_time  = (int)($start_time ?? time());
    $finish_time = time();
    $service_id  = (int)($service ?? 0);
    $user_id     = (int)($id ?? 0);
    $reply_date_only = date('Y-m-d'); // 📅 FIX: Use date only for counting

    $skip_charge = false; // 🔴 Flag to skip charging (for bulk mode)
    
    if ($status == 4) {
        if ($safe_costa > 0) {
            $check = $link->query("SELECT credit_left FROM tblUsers WHERE id = $user_id");
            $before = $check ? $check->fetch_assoc() : null;
            if (!$before || (float) $before['credit_left'] < $safe_costa) {
                return 0;
            }
        }
        $link->autocommit(false);
        $link->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
    } elseif ($status == 44) {
        $status = 4; // Reset to standard Success status for recording
        $skip_charge = true; // 🔴 SKIP charging - already charged in bulk
    }

    $qry = "INSERT INTO `tbl_order_imei` (
        `service_id`, `user_id`, `imei`, `admin_note`, `date`, `time`, `note`, `comments`, 
        `user_cost`, `credit`, `user_can_c`, `reply`, `reply_time`, 
        `reply_by_admin`, `reply_date`, `ip`, `pricefrom`,
        `adminnote`, `ORDERAPI`, `ORDERAPI_ID`, `ORDERAPI_TYPEID`, `apierror`, `acceptedtime`, `acceptedby`, `discount`
    ) VALUES (
        $service_id, $user_id, '$safe_imei', '$safe_note', '$safe_date', '$order_time', '$safe_note', '', 
        $safe_costa, $safe_costa, $status, '$safe_result', '$finish_time', 
        1, '$reply_date_only', '$safe_ip', 'PHP API',
        '', '', '', '', '', '$order_time', 1, 0.000
    )";

    if ($link->query($qry)) {
        $order_id = $link->insert_id;
        if ($status == 4 && !$skip_charge) { // Standard charge logic (NOT for bulk)
            if ($safe_costa > 0) {
                $upd = "UPDATE tblUsers SET credit_left = credit_left - $safe_costa, credit_used = credit_used + $safe_costa WHERE id = $user_id AND credit_left >= $safe_costa";
                if ($link->query($upd) && $link->affected_rows > 0) {
                    $link->commit();
                    $link->autocommit(true);

                    return $order_id;
                }
                $link->rollback();
                $link->autocommit(true);

                return 0;
            }
            $link->commit();
            $link->autocommit(true);

            return $order_id;
        }
        return $order_id;
    } else {
        logEvent("SQL-INSERT-ERROR", $link->error . " Query: " . substr($qry, 0, 500));
    }
    if ($status == 4 && !$skip_charge) { $link->rollback(); $link->autocommit(true); }
    return 0;
}

function getUserCreditLeft($link, $user_id) {
    $user_id = (int) $user_id;
    if ($user_id <= 0) {
        return '0.000';
    }
    $res = $link->query("SELECT credit_left FROM tblUsers WHERE id = $user_id LIMIT 1");
    $row = $res ? $res->fetch_assoc() : null;
    return number_format((float) ($row['credit_left'] ?? 0), 3, '.', '');
}

/**
 * Block paid orders when credit_left is below service price. Free (0) services skip balance check.
 *
 * @return array{ok:bool,error?:string,balance?:string,required?:string,service_error?:bool}
 */
function phpApiAssertBalanceForPrice($link, $userId, $price) {
    if ($price === null || $price < 0 || $price >= 99999) {
        return ['ok' => false, 'error' => 'Service disabled', 'service_error' => true];
    }
    if ((float) $price === 0.0) {
        return ['ok' => true, 'balance' => getUserCreditLeft($link, $userId)];
    }
    $userId = (int) $userId;
    $res = $link->query("SELECT credit_left FROM tblUsers WHERE id = $userId LIMIT 1");
    $row = $res ? $res->fetch_assoc() : null;
    $balance = (float) ($row['credit_left'] ?? 0);
    $price = (float) $price;
    if ($balance + 1e-9 < $price) {
        return [
            'ok' => false,
            'error' => 'Insufficient balance. Required: $' . number_format($price, 3, '.', '') . ', Available: $' . number_format($balance, 3, '.', ''),
            'balance' => number_format($balance, 3, '.', ''),
            'required' => number_format($price, 3, '.', ''),
        ];
    }

    return ['ok' => true, 'balance' => number_format($balance, 3, '.', '')];
}

/** Authenticate PHP API key — same rules as place-order flow. */
function phpApiAuthenticateUser($connect, $api_key, $skipIpCheck = false) {
    $settingsPath = dirname(__DIR__, 2) . '/includes/public_api_settings.php';
    if (is_file($settingsPath)) {
        require_once $settingsPath;
        if (!public_api_settings_is_php_enabled($connect)) {
            return ['ok' => false, 'error' => 'PHP API is temporarily unavailable'];
        }
    }
    $clientIp = getClientIP();
    $safe_key = $connect->real_escape_string((string) $api_key);
    $userRes = $connect->query("SELECT * FROM tblUsers WHERE api_key_php = '$safe_key' LIMIT 1");
    $user = $userRes ? $userRes->fetch_assoc() : null;
    if (!$user) {
        return ['ok' => false, 'error' => 'Authentication Failed'];
    }
    if (intval($user['api_access_php'] ?? 0) !== 1) {
        return ['ok' => false, 'error' => 'PHP API Disabled'];
    }
    // Balance / service list are read-only — never block on credit or IP whitelist.
    if (!$skipIpCheck && !isIpAllowed($user['api_ip_php'] ?? '', $clientIp)) {
        return ['ok' => false, 'error' => "Unauthorized IP Address: $clientIp"];
    }
    return ['ok' => true, 'user' => $user, 'ip' => $clientIp];
}

function phpApiSanitizeUtf8($value) {
    if (!is_string($value)) {
        return $value;
    }
    if ($value === '') {
        return '';
    }
    if (function_exists('mb_convert_encoding')) {
        $clean = @mb_convert_encoding($value, 'UTF-8', 'UTF-8');
        if (is_string($clean) && $clean !== '') {
            return $clean;
        }
    }
    $clean = @iconv('UTF-8', 'UTF-8//IGNORE', $value);
    return is_string($clean) ? $clean : '';
}

function phpApiSanitizeUtf8Deep($data) {
    if (is_array($data)) {
        foreach ($data as $k => $v) {
            $data[$k] = phpApiSanitizeUtf8Deep($v);
        }
        return $data;
    }
    if (is_string($data)) {
        return phpApiSanitizeUtf8($data);
    }
    return $data;
}

function phpApiAccountJsonResponse($success, $reply = null, $error = '') {
    header('Content-Type: application/json; charset=utf-8');
    $payload = $success
        ? ['status' => 'Success', 'reply' => $reply ?? new stdClass()]
        : ['status' => 'Error', 'reply' => $error !== '' ? phpApiSanitizeUtf8((string) $error) : 'Request failed'];
    $payload = phpApiSanitizeUtf8Deep($payload);
    $flags = JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
    if (defined('JSON_INVALID_UTF8_SUBSTITUTE')) {
        $flags |= JSON_INVALID_UTF8_SUBSTITUTE;
    }
    $json = json_encode($payload, $flags);
    if ($json === false) {
        logEvent('JSON-ENCODE-FAIL', json_last_error_msg());
        $json = json_encode([
            'status' => 'Error',
            'reply' => 'Failed to encode service list response',
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }
    die($json);
}

/** Endpoint config for output-format hints (api/endpoints.json). */
function phpApiGetEndpointMeta($serviceId) {
    static $map = null;
    if ($map === null) {
        $map = [];
        $file = dirname(__DIR__) . '/endpoints.json';
        if (is_file($file)) {
            $cfg = json_decode((string) file_get_contents($file), true);
            if (is_array($cfg['services'] ?? null)) {
                $map = $cfg['services'];
            }
        }
    }
    return $map[(string) (int) $serviceId] ?? null;
}

/** IMEI | SN | IMEI/SN — from service input settings. */
function phpApiResolveInputType($minLen, $maxLen, $allowAlpha) {
    $minLen = (int) $minLen;
    $maxLen = (int) $maxLen;
    if ($maxLen <= 0) {
        $maxLen = 15;
    }
    if ($minLen <= 0) {
        $minLen = 15;
    }
    $acceptsImei = ($minLen <= 15 && $maxLen >= 15);
    $acceptsSerial = ($allowAlpha === 1 || $minLen !== 15 || $maxLen !== 15);
    if ($acceptsImei && $acceptsSerial) {
        return 'IMEI/SN';
    }
    if ($acceptsSerial) {
        return 'SN';
    }
    return 'IMEI';
}

/** Whether order reply supports structured JSON (json=true) and/or HTML text (default). */
function phpApiResolveFormatSupport($serviceId) {
    $ep = phpApiGetEndpointMeta($serviceId);
    if (!is_array($ep)) {
        return ['jsonSupport' => true, 'htmlSupport' => true];
    }
    $nativeJson = !empty($ep['params']['json']);
    if ($nativeJson) {
        return ['jsonSupport' => true, 'htmlSupport' => false];
    }
    return ['jsonSupport' => true, 'htmlSupport' => true];
}

/** Service list reply — keyed by service ID. */
function phpApiBuildServiceListReply(array $services) {
    $reply = [];
    foreach ($services as $s) {
        $id = (string) (int) ($s['id'] ?? $s['service_id'] ?? 0);
        if ($id === '0') {
            continue;
        }
        $format = phpApiResolveFormatSupport((int) $id);
        $reply[$id] = [
            'name' => (string) ($s['name'] ?? $s['service_name'] ?? ''),
            'price' => (string) ($s['price'] ?? '0.000'),
            'time' => (string) ($s['delivery_time'] ?? 'Instant'),
            'input' => (string) ($s['input_type'] ?? 'IMEI'),
            'jsonSupport' => !empty($format['jsonSupport']),
            'htmlSupport' => !empty($format['htmlSupport']),
        ];
    }
    return $reply;
}

/** Whether tbl_service_status exists (per-group enable/disable). */
function phpApiServiceStatusTableExists($connect) {
    static $exists = null;
    if ($exists !== null) {
        return $exists;
    }
    $checkTable = $connect->query("SHOW TABLES LIKE 'tbl_service_status'");
    $exists = ($checkTable && $checkTable->num_rows > 0);
    return $exists;
}

/** Active instant-only services: active + status + service_type 0 + enabled for client group. */
function phpApiInstantServiceWhereSql($groupId, $statusTableExists, $serviceId = null) {
    $grp_int = (int) $groupId;
    $idFilter = $serviceId !== null ? ' AND s.id = ' . (int) $serviceId : '';
    $baseWhere = "s.active = 1 AND s.status = 1 AND IFNULL(s.service_type, 0) = 0$idFilter";
    if ($statusTableExists) {
        return "INNER JOIN tbl_service_status ss ON s.id = ss.serviceid AND ss.groupid = $grp_int AND ss.status = 1
                WHERE $baseWhere";
    }
    return "WHERE $baseWhere";
}

function phpApiIsInstantServiceAvailable($connect, $serviceId, $groupId) {
    $serviceId = (int) $serviceId;
    $groupId = (int) $groupId;
    if ($serviceId <= 0) {
        return false;
    }
    $joinWhere = phpApiInstantServiceWhereSql($groupId, phpApiServiceStatusTableExists($connect), $serviceId);
    $sql = "SELECT s.id FROM tbl_services_imei s $joinWhere LIMIT 1";
    $result = $connect->query($sql);
    return ($result && $result->num_rows > 0);
}

/** Instant services for the user's group — same catalog as dashboard PHP API test list. */
function phpApiBuildInstantServiceList($connect, $uid, $groupId) {
    $uid = (int) $uid;
    $grp_int = (int) $groupId;
    $statusTableExists = phpApiServiceStatusTableExists($connect);
    $joinWhere = phpApiInstantServiceWhereSql($grp_int, $statusTableExists);

    $sql = "SELECT s.id, s.service_name, s.time as delivery_time, s.credit, s.info, s.group_id, s.service_type,
                   s.imei_custom_name, s.imei_custom_len, s.maxlength, s.isalpha,
                   COALESCE(g.group_name, 'Other Services') as group_name
            FROM tbl_services_imei s
            LEFT JOIN tbl_imei_group g ON s.group_id = g.id AND (g.active = 1 OR g.id IS NULL)
            $joinWhere
            ORDER BY g.group_name, s.service_name";

    $result = $connect->query($sql);
    $services = [];
    if (!$result) {
        return $services;
    }

    $rawRows = [];
    while ($row = $result->fetch_assoc()) {
        $rawRows[] = $row;
    }

    $serviceIds = array_map(static function ($row) {
        return (int) $row['id'];
    }, $rawRows);
    $priceCache = phpApiBatchServicePrices($connect, $uid, $grp_int, $serviceIds);

    foreach ($rawRows as $row) {
        $serviceId = (int) $row['id'];
        $price = $priceCache[$serviceId] ?? null;
        if ($price === null || $price < 0 || $price >= 99999) {
            continue;
        }
        $minLen = isset($row['imei_custom_len']) ? (int) $row['imei_custom_len'] : 0;
        $maxLen = isset($row['maxlength']) ? (int) $row['maxlength'] : 0;
        $allowAlpha = isset($row['isalpha']) ? (int) $row['isalpha'] : 0;
        $services[] = [
            'id' => $serviceId,
            'service_id' => $serviceId,
            'name' => $row['service_name'],
            'service_name' => $row['service_name'],
            'price' => number_format((float) $price, 3, '.', ''),
            'delivery_time' => !empty($row['delivery_time']) ? $row['delivery_time'] : 'Instant',
            'group_name' => !empty($row['group_name']) ? $row['group_name'] : 'Other Services',
            'input_type' => phpApiResolveInputType($minLen, $maxLen, $allowAlpha),
        ];
    }

    return $services;
}

function phpApiServiceListHtml(array $services) {
    if (empty($services)) {
        return 'No instant services available for your account.';
    }
    $rows = '';
    foreach ($services as $s) {
        $name = htmlspecialchars((string) ($s['name'] ?? ''), ENT_QUOTES, 'UTF-8');
        $price = htmlspecialchars((string) ($s['price'] ?? '0.000'), ENT_QUOTES, 'UTF-8');
        $time = htmlspecialchars((string) ($s['delivery_time'] ?? 'Instant'), ENT_QUOTES, 'UTF-8');
        $input = htmlspecialchars((string) ($s['input_type'] ?? 'IMEI'), ENT_QUOTES, 'UTF-8');
        $json = !empty($s['jsonSupport']) ? 'Yes' : 'No';
        $html = !empty($s['htmlSupport']) ? 'Yes' : 'No';
        $rows .= '<tr><td>' . (int) ($s['id'] ?? 0) . '</td><td>' . $name . '</td><td>$' . $price . '</td><td>' . $time . '</td><td>' . $input . '</td><td>' . $json . '</td><td>' . $html . '</td></tr>';
    }
    return '<table border="1" cellpadding="4"><thead><tr><th>ID</th><th>Service</th><th>Price</th><th>Time</th><th>Input</th><th>JSON</th><th>HTML</th></tr></thead><tbody>' . $rows . '</tbody></table>';
}

function processDirectIMEI($link, $user, $serviceId, $imei, $ip, $start_time) {
    $price = getServicePrice($link, $serviceId, $user['id'], $user['client_group']);
    $date_now = date('Y-m-d');
    if ($price === null || $price < 0 || $price >= 99999) {
        return ['success' => false, 'order_id' => 0, 'error' => 'Service disabled', 'raw_response' => ''];
    }
    $balanceCheck = phpApiAssertBalanceForPrice($link, $user['id'], $price);
    if (!$balanceCheck['ok']) {
        return [
            'success' => false,
            'order_id' => 0,
            'error' => $balanceCheck['error'],
            'balance' => $balanceCheck['balance'] ?? getUserCreditLeft($link, $user['id']),
            'required' => $balanceCheck['required'] ?? null,
            'raw_response' => '',
        ];
    }

    $apiRes = callEndpointWithFallback($serviceId, $imei);
    if (empty($apiRes['response'])) {
        $oid = insertOrderAndCharge($link, $serviceId, $user['id'], $imei, 'Rejected', 'Wrong IMEI or Service Offline!', 0, $ip, $start_time, $date_now, 3);
        return ['success' => false, 'order_id' => $oid, 'error' => 'Wrong IMEI or Service Offline!', 'raw_response' => ''];
    }
    $isValid = $apiRes['success']; $normalized = normalizeResult($apiRes['response'], $imei);
    if (!$isValid) {
        $oid = insertOrderAndCharge($link, $serviceId, $user['id'], $imei, 'Rejected', $normalized, 0, $ip, $start_time, $date_now, 3);
        return ['success' => false, 'order_id' => $oid, 'error' => 'Wrong IMEI or Service Offline!', 'raw' => $normalized, 'raw_response' => $apiRes['response']];
    }
    $order_id = insertOrderAndCharge($link, $serviceId, $user['id'], $imei, 'Success', $normalized, $price, $ip, $start_time, $date_now, 4);
    if ($order_id > 0) {
        $balance = getUserCreditLeft($link, $user['id']);
        return [
            'success' => true,
            'order_id' => $order_id,
            'result' => $normalized,
            'price' => number_format($price, 3, '.', ''),
            'balance' => $balance,
            'raw_response' => $apiRes['response']
        ];
    }
    return [
        'success' => false,
        'order_id' => 0,
        'error' => 'Transaction failed or insufficient balance',
        'balance' => getUserCreditLeft($link, $user['id']),
        'raw_response' => $apiRes['response'],
    ];
}

// ---------------------------------------------------------
// 3. MAIN LOGIC (Direct & Bulk Modes)
// ---------------------------------------------------------

$raw_input = file_get_contents('php://input');
$json_input = json_decode($raw_input, true);
$is_bulk = (is_array($json_input) && !empty($json_input) && isset($json_input[0]['imei']));

$api_key = $_GET['api_key_php'] ?? $_POST['api_key_php'] ?? $_GET['api_key'] ?? $_POST['api_key'] ?? $_GET['api'] ?? $_POST['api'] ?? $_GET['key'] ?? $_POST['key'] ?? '';
$service = $_GET['service_id'] ?? $_POST['service_id'] ?? $_GET['service'] ?? $_POST['service'] ?? '';
$imei = $_GET['imei'] ?? $_POST['imei'] ?? '';
$accountinfo = $_GET['accountinfo'] ?? $_POST['accountinfo'] ?? '';
$is_json = (($_GET['json'] ?? $_POST['json'] ?? '') === 'true' || ($_GET['json'] ?? $_POST['json'] ?? '') === '1' || strtoupper($_GET['requestformat'] ?? $_POST['requestformat'] ?? '') === 'JSON' || strtoupper($_GET['RequestFormat'] ?? $_POST['RequestFormat'] ?? '') === 'JSON');

if (empty($api_key) && is_array($json_input) && !$is_bulk) {
    $api_key = $json_input['api_key_php'] ?? $json_input['api_key'] ?? $json_input['api'] ?? $json_input['key'] ?? '';
    $service = $json_input['service_id'] ?? $json_input['service'] ?? '';
    $imei = $json_input['imei'] ?? '';
    $accountinfo = $json_input['accountinfo'] ?? $accountinfo;
    if (isset($json_input['json'])) $is_json = ($json_input['json'] === true || $json_input['json'] === 'true' || $json_input['json'] === 1 || $json_input['json'] === '1');
}

if (function_exists('client_api_logs_register_php_snapshot')) {
    client_api_logs_register_php_snapshot($_GET, $_POST, $raw_input);
}
$GLOBALS['_client_api_php_raw_body'] = $raw_input;

// Account metadata — balance / service list
if (!empty($api_key) && !$is_bulk) {
    $accountAction = strtolower(trim((string) $accountinfo));
    if ($accountAction === '') {
        $rawAction = strtolower(trim((string) ($_GET['action'] ?? $_POST['action'] ?? ($json_input['action'] ?? ''))));
        if (in_array($rawAction, ['balance', 'servicelist', 'service_list', 'services'], true)) {
            $accountAction = $rawAction === 'services' || $rawAction === 'service_list' ? 'servicelist' : $rawAction;
        }
    }

    if ($accountAction !== '') {
        @set_time_limit(25);
        $auth = phpApiAuthenticateUser($connect, $api_key, true);
        if (!$auth['ok']) {
            phpApiWriteClientLog($connect, [
                'endpoint' => $accountAction ?: 'account',
                'status' => 'auth_failed',
                'error_message' => $auth['error'],
            ]);
            phpApiAccountJsonResponse(false, null, $auth['error']);
        }
        $user = $auth['user'];

        if ($accountAction === 'balance') {
            $balance = getUserCreditLeft($connect, $user['id']);
            phpApiWriteClientLog($connect, [
                'user_id' => (int) $user['id'],
                'username' => $user['username'] ?? '',
                'endpoint' => 'balance',
                'status' => 'success',
            ]);
            phpApiAccountJsonResponse(true, [
                'account_balance' => $balance,
                'currency' => 'USD',
            ]);
        }

        if (in_array($accountAction, ['servicelist', 'services', 'service_list'], true)) {
            $groupId = isset($user['client_group']) ? (int) $user['client_group'] : 0;
            try {
                $services = phpApiBuildInstantServiceList($connect, (int) $user['id'], $groupId);
                phpApiWriteClientLog($connect, [
                    'user_id' => (int) $user['id'],
                    'username' => $user['username'] ?? '',
                    'endpoint' => 'servicelist',
                    'status' => 'success',
                    'meta' => ['service_count' => count($services)],
                ]);
                phpApiAccountJsonResponse(true, phpApiBuildServiceListReply($services));
            } catch (Throwable $e) {
                logEvent('SERVICELIST-ERROR', $e->getMessage());
                phpApiWriteClientLog($connect, [
                    'user_id' => (int) $user['id'],
                    'username' => $user['username'] ?? '',
                    'endpoint' => 'servicelist',
                    'status' => 'error',
                    'error_message' => $e->getMessage(),
                ]);
                phpApiAccountJsonResponse(false, null, 'Service list temporarily unavailable');
            }
        }

        phpApiWriteClientLog($connect, [
            'user_id' => (int) ($user['id'] ?? 0),
            'username' => $user['username'] ?? '',
            'endpoint' => $accountAction ?: 'account',
            'status' => 'error',
            'error_message' => 'Invalid accountinfo',
        ]);
        phpApiAccountJsonResponse(false, null, 'Invalid accountinfo. Use balance or servicelist.');
    }
}

if (strpos($imei, ',') !== false && !$is_bulk) {
    $imei_list = array_filter(array_map('trim', explode(',', $imei)));
    if (count($imei_list) > 1) {
        $json_input = [];
        foreach ($imei_list as $single_imei) $json_input[] = ['imei' => $single_imei];
        $is_bulk = true;
    }
}

if ($is_bulk) {
    // 🚀 Ensure response gets to client even for long operations
    if ($is_json) {
        header('Content-Type: application/json; charset=utf-8');
    } else {
        header('Content-Type: application/json');
    }
    header('X-Accel-Buffering: no'); // Disable Nginx buffering
    header('Cache-Control: no-cache');
    
    // Disable output buffering
    while (ob_get_level()) ob_end_clean();
    
    // Set Apache/PHP to not timeout
    @set_time_limit(0);
    @ignore_user_abort(true);
    
    $bulkStartTime = microtime(true);
    $totalItems = count($json_input);
    logEvent("BULK-START", "Processing $totalItems items");
    
    $results = []; 
    $clientIp = getClientIP();
    $urlMap = []; 
    $valid_indices = []; 
    $endpointConfigs = [];
    $networkErrors = []; // 🔴 Track network failures separately
    
    // 🚀 OPTIMIZATION: Authenticate User Once for Bulk
    $main_api_key = $api_key;
    if (empty($main_api_key) && !empty($json_input)) {
        $main_api_key = $json_input[0]['api_key_php'] ?? $json_input[0]['api_key'] ?? $json_input[0]['api'] ?? '';
    }
    
    $safe_key = $connect->real_escape_string($main_api_key);
    $userRes = $connect->query("SELECT * FROM tblUsers WHERE api_key_php = '$safe_key' LIMIT 1");
    $global_user = $userRes ? $userRes->fetch_assoc() : null;
    
    if (!$global_user) {
        logEvent("BULK-AUTH-FAIL", "API key not found");
        phpApiWriteClientLog($connect, [
            'endpoint' => 'bulk_order',
            'status' => 'auth_failed',
            'error_message' => 'Authentication Failed: API key not found',
            'meta' => ['items' => $totalItems],
        ]);
        die(json_encode(['error' => 'Authentication Failed: API key not found']));
    }
    
    $priceCache = []; // 🚀 Cache prices by service_id

    foreach ($json_input as $idx => $item) {
        $item_api_key = $item['api_key_php'] ?? $item['api_key'] ?? $item['api'] ?? $api_key;
        $item_service = $item['service_id'] ?? $item['service'] ?? $service;
        $item_imei_raw = $item['imei'] ?? '';
        $client_ref = $item['order_id'] ?? '';
        
        if (trim((string) $item_imei_raw) === '') { 
            $results[$idx] = ['order_id' => $client_ref, 'imei' => '', 'status' => 'Rejected', 'reply' => 'IMEI Required']; 
            continue; 
        }

        if (function_exists('php_api_validate_input_for_service')) {
            $inputCheck = php_api_validate_input_for_service($connect, $item_service, $item_imei_raw);
            if (empty($inputCheck['ok'])) {
                $results[$idx] = [
                    'order_id' => $client_ref,
                    'imei' => trim((string) $item_imei_raw),
                    'status' => 'Rejected',
                    'reply' => $inputCheck['error'] ?? 'Invalid input',
                ];
                continue;
            }
            $item_imei = $inputCheck['value'];
        } else {
            $item_imei = trim((string) $item_imei_raw);
        }
        
        // Use global user if API key matches, else re-authenticate (rare)
        $user = $global_user;
        if ($item_api_key !== $main_api_key) {
            $item_safe_key = $connect->real_escape_string($item_api_key);
            $itemUserRes = $connect->query("SELECT * FROM tblUsers WHERE api_key_php = '$item_safe_key' LIMIT 1");
            $user = $itemUserRes ? $itemUserRes->fetch_assoc() : null;
        }

        if (!$user) { 
            $results[$idx] = ['order_id' => $client_ref, 'imei' => $item_imei, 'status' => 'Rejected', 'reply' => 'Auth Failed: Key not found']; 
            continue; 
        }
        if (intval($user['api_access_php'] ?? 0) !== 1) { 
            $results[$idx] = ['order_id' => $client_ref, 'imei' => $item_imei, 'status' => 'Rejected', 'reply' => 'PHP API Disabled']; 
            continue; 
        }
        if (!isIpAllowed($user['api_ip_php'] ?? '', $clientIp)) { 
            $results[$idx] = ['order_id' => $client_ref, 'imei' => $item_imei, 'status' => 'Rejected', 'reply' => "Unauthorized IP Address: $clientIp"]; 
            continue; 
        }
        
        if (!phpApiIsInstantServiceAvailable($connect, $item_service, $user['client_group'])) {
            $results[$idx] = ['order_id' => $client_ref, 'imei' => $item_imei, 'status' => 'Rejected', 'reply' => 'Service disabled or not available'];
            continue;
        }

        $endpoint = getEndpointConfig($item_service);
        if (!$endpoint) {
            $results[$idx] = ['order_id' => $client_ref, 'imei' => $item_imei, 'status' => 'Rejected', 'reply' => 'Service not configured'];
            continue;
        }
        
        $endpointConfigs[$idx] = $endpoint;
        $hasLink2 = isset($endpoint['link2']) && is_array($endpoint['link2']) && !empty($endpoint['link2']['url']);
        
        // 🚀 PRE-CACHE PRICE
        if (!isset($priceCache[$item_service])) {
            $priceCache[$item_service] = getServicePrice($connect, $item_service, $user['id'], $user['client_group']);
        }
        
        // 🕐 Track timeout for this endpoint
        $endpointTimeout = (int)($endpoint['timeout'] ?? 30);
        if (!isset($maxTimeout) || $endpointTimeout > $maxTimeout) {
            $maxTimeout = $endpointTimeout;
        }

        // Check if link2_mode is "only" - use link2 directly
        if ($hasLink2 && ($endpoint['link2_mode'] ?? 'fallback') === 'only') {
            $urlMap[$idx] = str_replace('{imei}', $item_imei, $endpoint['link2']['url']);
        } else {
            $urlMap[$idx] = str_replace('{imei}', $item_imei, $endpoint['url'] ?? '');
        }
        $valid_indices[$idx] = $user;
    }
    
    // Use max timeout from all endpoints (minimum 30s, maximum 120s)
    $maxTimeout = isset($maxTimeout) ? min(max($maxTimeout, 30), 120) : 45;
    
    logEvent("BULK-PREP", "Prepared " . count($urlMap) . " URLs for requests (timeout: {$maxTimeout}s)");
    
    // First batch of requests (link1 or link2 if mode is "only")
    $responses = multiCURL($urlMap, 20, $maxTimeout);
    
    logEvent("BULK-FIRST-BATCH", "First batch complete, got " . count($responses) . " responses");
    
    // Check which ones failed and need link2 fallback
    $link2Map = [];
    $link2Indices = [];
    foreach ($json_input as $idx => $item) {
        if (isset($results[$idx])) continue;
        if (!isset($valid_indices[$idx])) continue;
        
        $item_service = $item['service_id'] ?? $item['service'] ?? $service;
        $item_imei = trim($item['imei'] ?? '');
        $endpoint = $endpointConfigs[$idx] ?? null;
        if (!$endpoint) continue;
        
        $raw_res = $responses[$idx] ?? '';
        
        // 🔴 Track empty responses as network errors
        if (empty($raw_res)) {
            $networkErrors[$idx] = true;
        }
        
        $hasLink2 = isset($endpoint['link2']) && is_array($endpoint['link2']) && !empty($endpoint['link2']['url']);
        if ($hasLink2 && ($endpoint['link2_mode'] ?? 'fallback') !== 'only') {
            if (!validateEndpointResult($item_service, $raw_res, $item_imei, '1')) {
                $link2Map[$idx] = str_replace('{imei}', $item_imei, $endpoint['link2']['url']);
                $link2Indices[] = $idx;
            }
        }
    }
    
    if (!empty($link2Map)) {
        logEvent("BULK-LINK2", "Trying link2 fallback for " . count($link2Map) . " items (timeout: {$maxTimeout}s)");
        try {
            $link2Responses = multiCURL($link2Map, 20, $maxTimeout);
            logEvent("BULK-LINK2-DONE", "Link2 batch complete, got " . count($link2Responses) . " responses");
            foreach ($link2Indices as $idx) {
                if (isset($link2Responses[$idx])) {
                    $responses[$idx] = $link2Responses[$idx];
                    // If link2 succeeded, remove from network errors
                    if (!empty($link2Responses[$idx])) {
                        unset($networkErrors[$idx]);
                    }
                }
            }
        } catch (Exception $e) {
            logEvent("BULK-LINK2-ERROR", "Link2 failed: " . $e->getMessage());
        }
    }
    
    // 🚀 BATCH DATABASE PROCESSING
    $successfulOrders = [];
    $totalCost = 0;
    $validationFails = [];
    
    foreach ($json_input as $idx => $item) {
        if (isset($results[$idx])) continue;
        if (!isset($valid_indices[$idx])) continue;
        
        $item_service = $item['service_id'] ?? $item['service'] ?? $service;
        $item_imei = trim($item['imei'] ?? '');
        $raw_res = $responses[$idx] ?? '';
        $endpoint = $endpointConfigs[$idx] ?? null;
        $usedLink2 = in_array($idx, $link2Indices) || ($endpoint && ($endpoint['link2_mode'] ?? '') === 'only');
        
        // 🔴 Skip network errors - don't validate empty responses
        if (isset($networkErrors[$idx]) || empty($raw_res)) {
            $validationFails[$idx] = 'network_error';
            continue;
        }
        
        if (validateEndpointResult($item_service, $raw_res, $item_imei, $usedLink2 ? '2' : '1')) {
            $price = $priceCache[$item_service] ?? null;
            if ($price !== null && $price >= 0 && $price < 99999) {
                $successfulOrders[$idx] = ['price' => $price, 'res' => $raw_res, 'norm' => normalizeResult($raw_res, $item_imei)];
                $totalCost += $price;
            } else {
                $validationFails[$idx] = 'price_invalid';
            }
        } else {
            $validationFails[$idx] = 'validation_fail';
        }
    }

    logEvent("BULK-VALIDATED", "Successful: " . count($successfulOrders) . " | Failed: " . count($validationFails) . " | Network errors: " . count($networkErrors) . " | Total cost: $totalCost");

    // Single Balance Check and Update (skip when all successes are free: totalCost === 0)
    $canProceed = false;
    $freeSuccessCount = count($successfulOrders);
    if ($totalCost > 0 && $global_user) {
        logEvent("BULK-BALANCE-CHECK", "Checking balance for user {$global_user['id']}, need: $totalCost");
        try {
            $connect->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
            $check = $connect->query("SELECT credit_left FROM tblUsers WHERE id = {$global_user['id']} FOR UPDATE");
            $bal = $check ? $check->fetch_assoc() : null;
            if ($bal && (float) $bal['credit_left'] >= $totalCost) {
                $connect->query("UPDATE tblUsers SET credit_left = credit_left - $totalCost, credit_used = credit_used + $totalCost WHERE id = {$global_user['id']}");
                $connect->commit();
                $canProceed = true;
                logEvent("BULK-BALANCE", "Charged $totalCost from user {$global_user['id']}");
            } else {
                $connect->rollback();
                logEvent("BULK-BALANCE-FAIL", "Insufficient balance for user {$global_user['id']}. Required: $totalCost, Available: " . ($bal['credit_left'] ?? 0));
            }
        } catch (Exception $e) {
            logEvent("BULK-BALANCE-ERROR", "Exception: " . $e->getMessage());
            @$connect->rollback();
        }
    } elseif ($totalCost == 0 && $freeSuccessCount > 0 && $global_user) {
        $canProceed = true;
        logEvent("BULK-ZERO-CHARGE", "Proceeding with {$freeSuccessCount} free (0 credit) order(s) for user {$global_user['id']}");
    } elseif ($totalCost == 0) {
        logEvent("BULK-NO-SUCCESS", "No successful orders to process (totalCost=0)");
    }

    // Final loop to record all results
    foreach ($json_input as $idx => $item) {
        if (isset($results[$idx])) continue;
        
        if (!isset($valid_indices[$idx])) {
            // Already rejected during validation phase
            continue;
        }
        
        $user = $valid_indices[$idx]; 
        $item_service = $item['service_id'] ?? $item['service'] ?? $service;
        $item_imei = trim($item['imei'] ?? ''); 
        $client_ref = $item['order_id'] ?? '';
        $item_is_json = isset($item['json']) ? ($item['json'] === true || $item['json'] === 'true' || $item['json'] === 1 || $item['json'] === '1') : $is_json;
        
        // 🟢 SUCCESS: Valid response + balance available
        if (isset($successfulOrders[$idx]) && $canProceed) {
            $order = $successfulOrders[$idx];
            $order_id = insertOrderAndCharge($connect, $item_service, $user['id'], $item_imei, 'Success', $order['norm'], $order['price'], $clientIp, $request_start_time, date('Y-m-d'), 44);
            $results[$idx] = [
                'order_id' => !empty($client_ref) ? $client_ref : $order_id, 
                'imei' => $item_imei, 
                'status' => 'Success', 
                'price' => number_format($order['price'], 3, '.', ''), 
                'order_reference_id' => $order_id, 
                'reply' => $item_is_json ? parseCoverageResultToGroupedReply($order['res']) : formatRawToText($order['res'])
            ];
        }
        // 🔴 NETWORK ERROR: Don't insert into database - return error without order
        elseif (isset($networkErrors[$idx])) {
            // 🚨 IMPORTANT: Don't insert rejected order for network errors
            // This allows the user to retry without duplicate orders
            $results[$idx] = [
                'order_id' => $client_ref, 
                'imei' => $item_imei, 
                'status' => 'Error', 
                'price' => '0.000', 
                'reply' => 'Network Error: Connection failed or server timeout. Please retry.'
            ];
            logEvent("BULK-NETWORK-ERR", "IMEI $item_imei - No order inserted due to network error");
        }
        // 🔴 INSUFFICIENT BALANCE: Don't insert into database — no order row
        elseif (isset($successfulOrders[$idx]) && !$canProceed) {
            $results[$idx] = [
                'order_id' => $client_ref,
                'imei' => $item_imei,
                'status' => 'Error',
                'price' => '0.000',
                'reply' => 'Insufficient balance',
            ];
            logEvent('BULK-BALANCE-ITEM', "IMEI $item_imei - No order inserted due to insufficient balance");
        }
        // 🟡 VALIDATION FAILED: Insert rejected order
        else {
            $failReason = $validationFails[$idx] ?? 'unknown';
            if ($failReason === 'price_invalid') {
                $err = 'Service disabled or price not set';
            } else {
                $err = 'Wrong IMEI or Service Offline!';
            }

            $oid = insertOrderAndCharge($connect, $item_service, $user['id'], $item_imei, 'Rejected', $err, 0, $clientIp, $request_start_time, date('Y-m-d'), 3);
            $results[$idx] = [
                'order_id' => !empty($client_ref) ? $client_ref : $oid,
                'imei' => $item_imei,
                'status' => 'Rejected',
                'price' => '0.000',
                'order_reference_id' => $oid,
                'reply' => $err,
            ];
        }
    }
    
    $bulkEndTime = microtime(true);
    $totalTime = round($bulkEndTime - $bulkStartTime, 2);
    
    // Count results safely
    $successCount = 0;
    $errorCount = 0;
    $rejectedCount = 0;
    foreach ($results as $r) {
        $status = $r['status'] ?? '';
        if ($status === 'Success') $successCount++;
        elseif ($status === 'Error') $errorCount++;
        elseif ($status === 'Rejected') $rejectedCount++;
    }
    
    logEvent("BULK-COMPLETE", "Processed $totalItems items in {$totalTime}s | Success: $successCount | Errors: $errorCount | Rejected: $rejectedCount");

    $remainingBalance = $global_user ? getUserCreditLeft($connect, $global_user['id']) : null;
    if ($remainingBalance !== null) {
        foreach ($results as $idx => $result) {
            if (($result['status'] ?? '') === 'Success') {
                $results[$idx]['balance'] = $remainingBalance;
            }
        }
    }
    
    // Sort and prepare output
    logEvent("JSON-START", "Preparing JSON response for " . count($results) . " items");
    
    ksort($results);
    
    // Encode JSON with error handling
    $jsonOutput = @json_encode(array_values($results), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    
    if ($jsonOutput === false || $jsonOutput === null) {
        $jsonError = json_last_error_msg();
        logEvent("JSON-ERROR", "JSON encode failed: $jsonError");
        
        // Try simpler encoding
        $simpleResults = [];
        foreach ($results as $idx => $r) {
            $simpleResults[] = [
                'order_id' => $r['order_id'] ?? '',
                'imei' => $r['imei'] ?? '',
                'status' => $r['status'] ?? 'Unknown',
                'price' => $r['price'] ?? '0.000'
            ];
        }
        $jsonOutput = json_encode($simpleResults, JSON_PRETTY_PRINT);
        
        if ($jsonOutput === false) {
            $jsonOutput = json_encode(['error' => 'Failed to encode response', 'success_count' => $successCount]);
        }
    }
    
    $length = strlen($jsonOutput);
    logEvent("JSON-READY", "JSON ready: $length bytes");
    
    // Send response
    header('Content-Length: ' . $length);
    header('Connection: close');
    
    logEvent("RESPONSE-SEND", "Sending $length bytes to client");
    
    echo $jsonOutput;
    
    // Force flush output
    @ob_end_flush();
    @flush();
    
    if (function_exists('fastcgi_finish_request')) {
        fastcgi_finish_request();
    }
    
    logEvent("RESPONSE-DONE", "Response sent successfully");

    $bulkDurationMs = (int) round((microtime(true) - $bulkStartTime) * 1000);
    foreach ($results as $idx => $r) {
        $itemStatus = strtolower((string) ($r['status'] ?? 'unknown'));
        $logStatus = $itemStatus === 'success' ? 'success' : ($itemStatus === 'error' ? 'error' : 'rejected');
        phpApiWriteClientLog($connect, [
            'user_id' => (int) ($global_user['id'] ?? 0),
            'username' => $global_user['username'] ?? '',
            'endpoint' => 'bulk_order',
            'search_term' => (string) ($r['imei'] ?? ''),
            'status' => $logStatus,
            'duration_ms' => $bulkDurationMs,
            'response_summary' => (string) ($r['reply'] ?? ''),
            'meta' => [
                'bulk_index' => $idx,
                'order_id' => $r['order_id'] ?? null,
                'price' => $r['price'] ?? null,
            ],
        ]);
    }
    
    exit;
}

if (!empty($api_key) && !empty($service)) {
    if (trim((string) $imei) === '') {
        phpApiWriteClientLog($connect, [
            'endpoint' => 'place_order',
            'status' => 'error',
            'error_message' => 'IMEI Required',
            'meta' => ['service_id' => $service],
        ]);
        header('Content-Type: application/json');
        die(json_encode(['error' => 'IMEI Required']));
    }
    if (function_exists('php_api_validate_input_for_service')) {
        $inputCheck = php_api_validate_input_for_service($connect, $service, $imei);
        if (empty($inputCheck['ok'])) {
            phpApiWriteClientLog($connect, [
                'endpoint' => 'place_order',
                'status' => 'error',
                'error_message' => $inputCheck['error'] ?? 'Invalid input',
                'meta' => ['service_id' => $service],
            ]);
            header('Content-Type: application/json');
            die(json_encode([
                'order_id' => 0,
                'imei' => trim((string) $imei),
                'status' => 'Rejected',
                'price' => '0.000',
                'reply' => $inputCheck['error'] ?? 'Invalid input',
            ]));
        }
        $imei = $inputCheck['value'];
    }
    $settingsPath = dirname(__DIR__, 2) . '/includes/public_api_settings.php';
    if (is_file($settingsPath)) {
        require_once $settingsPath;
        if (!public_api_settings_is_php_enabled($connect)) {
            phpApiWriteClientLog($connect, [
                'endpoint' => 'place_order',
                'search_term' => $imei,
                'status' => 'error',
                'error_message' => 'PHP API is temporarily unavailable',
                'meta' => ['service_id' => $service],
            ]);
            header('Content-Type: application/json');
            die(json_encode(['error' => 'PHP API is temporarily unavailable']));
        }
    }
    $api_key = $connect->real_escape_string($api_key);
    $user = $connect->query("SELECT * FROM tblUsers WHERE api_key_php = '$api_key' LIMIT 1")->fetch_assoc();
    if ($user && intval($user['api_access_php'] ?? 0) === 1 && isIpAllowed($user['api_ip_php'] ?? '', $clientIp = getClientIP())) {
        if (!phpApiIsInstantServiceAvailable($connect, $service, $user['client_group'])) {
            phpApiWriteClientLog($connect, [
                'user_id' => (int) $user['id'],
                'username' => $user['username'] ?? '',
                'endpoint' => 'place_order',
                'search_term' => $imei,
                'status' => 'rejected',
                'error_message' => 'Service disabled or not available',
                'meta' => ['service_id' => $service],
            ]);
            header('Content-Type: application/json');
            die(json_encode(['order_id' => 0, 'imei' => $imei, 'status' => 'Rejected', 'price' => '0.000', 'reply' => 'Service disabled or not available']));
        }
        $price = getServicePrice($connect, $service, $user['id'], $user['client_group']);
        $balanceCheck = phpApiAssertBalanceForPrice($connect, $user['id'], $price);
        if (!$balanceCheck['ok']) {
            if (!empty($balanceCheck['service_error'])) {
                phpApiWriteClientLog($connect, [
                    'user_id' => (int) $user['id'],
                    'username' => $user['username'] ?? '',
                    'endpoint' => 'place_order',
                    'search_term' => $imei,
                    'status' => 'rejected',
                    'error_message' => 'Service disabled',
                    'meta' => ['service_id' => $service],
                ]);
                header('Content-Type: application/json');
                die(json_encode([
                    'order_id' => 0,
                    'imei' => $imei,
                    'status' => 'Rejected',
                    'price' => '0.000',
                    'reply' => 'Service disabled',
                ]));
            }
            $insufficientPayload = [
                'error' => $balanceCheck['error'],
                'balance' => $balanceCheck['balance'] ?? getUserCreditLeft($connect, $user['id']),
                'required' => $balanceCheck['required'] ?? null,
            ];
            phpApiSetLogResponse($insufficientPayload);
            phpApiWriteClientLog($connect, [
                'user_id' => (int) $user['id'],
                'username' => $user['username'] ?? '',
                'endpoint' => 'place_order',
                'search_term' => $imei,
                'status' => 'error',
                'error_message' => $balanceCheck['error'],
                'meta' => ['service_id' => $service, 'required' => $balanceCheck['required'] ?? null],
            ]);
            header('Content-Type: application/json');
            die(json_encode($insufficientPayload));
        }

        $apiRes = callEndpointWithFallback($service, $imei);
        $normalized = normalizeResult($apiRes['response'], $imei);
        if (function_exists('api_client_sanitize_supplier_message')) {
            $normalized = api_client_sanitize_supplier_message($normalized);
        }
        
        if ($apiRes['success'] && $price !== null && $price >= 0 && $price < 99999) {
            $order_id = insertOrderAndCharge($connect, $service, $user['id'], $imei, 'Success', $normalized, $price, $clientIp, $request_start_time, date('Y-m-d'), 4);
            if ($order_id > 0) {
                $replyOut = $is_json ? parseCoverageResultToGroupedReply($apiRes['response']) : formatRawToText($apiRes['response']);
                if (function_exists('api_client_sanitize_supplier_message')) {
                    $replyOut = api_client_sanitize_supplier_message($replyOut);
                }
                $responsePayload = [
                    'order_id' => $order_id, 
                    'imei' => $imei, 
                    'order_reference_id' => $order_id, 
                    'status' => 'Success', 
                    'price' => number_format($price, 3, '.', ''), 
                    'balance' => getUserCreditLeft($connect, $user['id']),
                    'reply' => $replyOut
                ];
                phpApiSetLogResponse($responsePayload);
                phpApiWriteClientLog($connect, [
                    'user_id' => (int) $user['id'],
                    'username' => $user['username'] ?? '',
                    'endpoint' => 'place_order',
                    'search_term' => $imei,
                    'status' => 'success',
                    'meta' => ['service_id' => $service, 'order_id' => $order_id, 'price' => number_format($price, 3, '.', '')],
                ]);
                if ($is_json) {
                    apiJsonModeContentType();
                }
                die(json_encode($responsePayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
            }
            $insufficientPayload = [
                'error' => 'Insufficient balance. Required: $' . number_format((float) $price, 3, '.', '') . ', Available: $' . getUserCreditLeft($connect, $user['id']),
                'balance' => getUserCreditLeft($connect, $user['id']),
                'required' => number_format((float) $price, 3, '.', ''),
            ];
            phpApiSetLogResponse($insufficientPayload);
            phpApiWriteClientLog($connect, [
                'user_id' => (int) $user['id'],
                'username' => $user['username'] ?? '',
                'endpoint' => 'place_order',
                'search_term' => $imei,
                'status' => 'error',
                'error_message' => $insufficientPayload['error'],
                'meta' => ['service_id' => $service, 'required' => $insufficientPayload['required']],
            ]);
            header('Content-Type: application/json');
            die(json_encode($insufficientPayload));
        }
        $err = empty($apiRes['response'])
            ? 'Wrong IMEI or Service Offline!'
            : (($price === null || $price < 0 || $price >= 99999) ? 'Service disabled' : 'Wrong IMEI or Service Offline!');
        $oid = insertOrderAndCharge($connect, $service, $user['id'], $imei, 'Rejected', $err, 0, $clientIp, $request_start_time, date('Y-m-d'), 3);
        $rejectPayload = [
            'order_id' => $oid, 
            'imei' => $imei, 
            'status' => 'Rejected', 
            'price' => '0.000', 
            'reply' => $err
        ];
        phpApiSetLogResponse($rejectPayload);
        phpApiWriteClientLog($connect, [
            'user_id' => (int) $user['id'],
            'username' => $user['username'] ?? '',
            'endpoint' => 'place_order',
            'search_term' => $imei,
            'status' => 'rejected',
            'error_message' => $err,
            'meta' => ['service_id' => $service, 'order_id' => $oid],
        ]);
        if ($is_json) {
            apiJsonModeContentType();
        } else {
            header('Content-Type: application/json');
        }
        die(json_encode($rejectPayload, JSON_PRETTY_PRINT));
    }
    header('Content-Type: application/json');
    $clientIp = getClientIP();
    $err_msg = (!$user) ? 'Authentication Failed' : ((intval($user['api_access_php'] ?? 0) !== 1) ? 'PHP API Disabled' : "Unauthorized IP Address: $clientIp");
    $logStatus = (!$user || intval($user['api_access_php'] ?? 0) !== 1) ? 'auth_failed' : 'ip_denied';
    phpApiWriteClientLog($connect, [
        'user_id' => $user ? (int) $user['id'] : null,
        'username' => $user['username'] ?? '',
        'endpoint' => 'place_order',
        'search_term' => $imei,
        'status' => $logStatus,
        'error_message' => $err_msg,
        'meta' => ['service_id' => $service],
    ]);
    die(json_encode(['error' => $err_msg]));
}
phpApiWriteClientLog($connect, [
    'endpoint' => 'unknown',
    'status' => 'error',
    'error_message' => 'Invalid Request',
]);
die(json_encode(['error' => 'Invalid Request']));
