<?php
require_once 'includes/ProxyHelper.php';

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

function sendJson($data, $code = 200) {
    http_response_code((int)$code);
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function looksHtml($body) {
    $s = strtolower(ltrim((string)$body));
    if ($s === '') return false;
    return (strpos($s, '<!doctype') === 0) || (strpos($s, '<html') === 0) || (strpos($s, '<head') === 0) || (strpos($s, '<body') === 0) || (strpos($s, '<html') !== false);
}

function looksBlocked($body) {
    if (!looksHtml($body)) return false;
    $ls = strtolower((string)$body);
    return strpos($ls, 'window["bobcmn"]') !== false
        || strpos($ls, 'bdris_waf_policy') !== false
        || strpos($ls, 'common/bdris_vs') !== false
        || strpos($ls, 'request rejected') !== false
        || strpos($ls, 'access denied') !== false
        || strpos($ls, 'requested url was rejected') !== false
        || strpos((string)$body, 'TSPD_') !== false;
}

function looksLogin($body) {
    if (!looksHtml($body)) return false;
    $ls = strtolower((string)$body);
    return strpos($ls, '<title>login</title>') !== false || strpos($ls, 'login-page') !== false;
}

function normalizeDateString($d) {
    $d = trim((string)$d);
    if ($d === '') return '';
    if (preg_match('/^(\d{4}-\d{2}-\d{2})T/i', $d, $m)) {
        $d = $m[1];
    }
    $d = str_replace(['/', '.'], '-', $d);
    if (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $d, $m)) {
        return sprintf('%04d-%02d-%02d', (int)$m[1], (int)$m[2], (int)$m[3]);
    }
    if (preg_match('/^(\d{2})-(\d{2})-(\d{4})$/', $d, $m)) {
        return sprintf('%04d-%02d-%02d', (int)$m[3], (int)$m[2], (int)$m[1]);
    }
    $ts = strtotime($d);
    if ($ts !== false) {
        return date('Y-m-d', $ts);
    }
    return $d;
}


function serviceCfg($service) {
    $svc = strtolower(trim((string)$service));
    if ($svc === 'dr_cancel') {
        return [
            'svc' => 'dr',
            'kind' => 'cancel',
            'list_url' => 'https://bdris.gov.bd/api/dr/certificate-cancellation-applications/search',
            'referer_search' => 'https://bdris.gov.bd/admin/dr/certificate-cancellation-applications/search',
            'details_url' => 'https://bdris.gov.bd/api/dr/certificate-cancellation-application/details',
            'receive_url' => 'https://bdris.gov.bd/api/dr/certificate-cancellation-application/receive',
            'reject_url' => 'https://bdris.gov.bd/api/dr/certificate-cancellation-application/reject',
            'approve_pd_url' => null,
            'approve_wo_parent_url' => null,
            'register_url' => 'https://bdris.gov.bd/api/dr/certificate-cancellation/application/register',
            'approve_url' => 'https://bdris.gov.bd/api/dr/certificate-cancellation/application/approve'
        ];
    }
    if ($svc === 'br_cancel') {
        return [
            'svc' => 'br',
            'kind' => 'cancel',
            'list_url' => 'https://bdris.gov.bd/api/br/certificate-cancellation-applications/search',
            'referer_search' => 'https://bdris.gov.bd/admin/br/certificate-cancellation-applications/search',
            'details_url' => 'https://bdris.gov.bd/api/br/certificate-cancellation-application/details',
            'receive_url' => 'https://bdris.gov.bd/api/br/certificate-cancellation-application/receive',
            'reject_url' => 'https://bdris.gov.bd/api/br/certificate-cancellation-application/reject',
            'approve_pd_url' => null,
            'approve_wo_parent_url' => null,
            'register_url' => 'https://bdris.gov.bd/api/br/certificate-cancellation/application/register',
            'approve_url' => 'https://bdris.gov.bd/api/br/certificate-cancellation/application/approve'
        ];
    }
    if ($svc === 'dr_corr') {
        return [
            'svc' => 'dr',
            'kind' => 'corr',
            'list_url' => 'https://bdris.gov.bd/api/dr/correction-applications/search',
            'referer_search' => 'https://bdris.gov.bd/admin/dr/correction-applications/search',
            'details_url' => 'https://bdris.gov.bd/api/application/details',
            'receive_url' => null,
            'reject_url' => 'https://bdris.gov.bd/api/application/reject',
            'approve_pd_url' => null,
            'approve_wo_parent_url' => null,
            'register_url' => null,
            'corr_approve_high_url' => 'https://bdris.gov.bd/api/dr/correction/application/higher-authority-approve',
            'corr_approve_nat_url' => null,
            'corr_approve_dob_url' => null,
            'corr_correct_url' => 'https://bdris.gov.bd/api/dr/correction/application/correct'
        ];
    }
    if ($svc === 'br_corr') {
        return [
            'svc' => 'br',
            'kind' => 'corr',
            'list_url' => 'https://bdris.gov.bd/api/br/correction-applications/search',
            'referer_search' => 'https://bdris.gov.bd/admin/br/correction-applications/search',
            'details_url' => 'https://bdris.gov.bd/api/application/details',
            'receive_url' => null,
            'reject_url' => 'https://bdris.gov.bd/api/application/reject',
            'approve_pd_url' => null,
            'approve_wo_parent_url' => null,
            'register_url' => null,
            'corr_approve_high_url' => 'https://bdris.gov.bd/api/br/correction/application/approve-as-higher-authority',
            'corr_approve_nat_url' => 'https://bdris.gov.bd/api/br/correction/application/nationality-approve',
            'corr_approve_dob_url' => 'https://bdris.gov.bd/api/br/correction/application/special-dob-approve',
            'corr_correct_url' => 'https://bdris.gov.bd/api/br/correction/application/correct'
        ];
    }
    if ($svc === 'dr') {
        return [
            'svc' => 'dr',
            'kind' => 'reg',
            'list_url' => 'https://bdris.gov.bd/api/dr/applications/search',
            'referer_search' => 'https://bdris.gov.bd/admin/dr/applications/search',
            'details_url' => 'https://bdris.gov.bd/api/application/details',
            'receive_url' => 'https://bdris.gov.bd/api/application/receive',
            'reject_url' => 'https://bdris.gov.bd/api/application/reject',
            'approve_pd_url' => null,
            'approve_wo_parent_url' => null,
            'register_url' => 'https://bdris.gov.bd/api/dr/application/register'
        ];
    }
    return [
        'svc' => 'br',
        'kind' => 'reg',
        'list_url' => 'https://bdris.gov.bd/api/br/applications/search',
        'referer_search' => 'https://bdris.gov.bd/admin/br/applications/search',
        'details_url' => 'https://bdris.gov.bd/api/application/details',
        'receive_url' => 'https://bdris.gov.bd/api/application/receive',
        'reject_url' => 'https://bdris.gov.bd/api/application/reject',
        'approve_pd_url' => 'https://bdris.gov.bd/api/br/application/approve-in-possible-duplicate-status',
        'approve_wo_parent_url' => 'https://bdris.gov.bd/api/br/application/approve-without-parent-ubrn',
        'register_url' => 'https://bdris.gov.bd/api/br/application/register'
    ];
}

$action = $_POST['action'] ?? ($_GET['action'] ?? 'list');
$service = $_POST['service'] ?? ($_GET['service'] ?? 'br');
$cfg = serviceCfg($service);
$proxy = new ProxyHelper();

// Globally capture and cache officeId if present
$officeIdTry = (string)($_POST['officeId'] ?? ($_GET['officeId'] ?? ''));
if ($officeIdTry !== '') {
    $officeFile = $proxy->getActiveOfficeFile();
    if ($officeFile) {
        @file_put_contents($officeFile, $officeIdTry);
    }
}

$csrfOverride = trim((string)($_POST['csrfToken'] ?? ($_POST['_csrf'] ?? ($_GET['csrfToken'] ?? ($_GET['_csrf'] ?? '')))));
$baseHeaders = [
    'X-Requested-With: XMLHttpRequest',
    'client: bris',
    'Accept: application/json, text/javascript, */*; q=0.01'
];
if ($csrfOverride !== '') {
    $baseHeaders[] = 'X-CSRF-TOKEN: ' . $csrfOverride;
}

if ($action === 'counts') {
    $officeId = (string)($_POST['officeId'] ?? ($_GET['officeId'] ?? ''));
    if ($officeId === '') sendJson(['ok' => false, '_error' => 'Office ID missing'], 400);

    $statusesRaw = (string)($_POST['statuses'] ?? ($_GET['statuses'] ?? ''));
    $statuses = [];
    foreach (preg_split('/\s*,\s*/', $statusesRaw) as $st) {
        $st = strtoupper(trim((string)$st));
        $st = preg_replace('/[^A-Z_]/', '', $st);
        if ($st !== '') $statuses[] = $st;
    }
    if (empty($statuses)) {
        $statuses = ['APPLIED', 'RECEIVED', 'REGISTERED', 'REJECTED', 'POSSIBLE_DUPLICATE', 'REQUIRES_APPROVAL_WHEN_PARENT_UNKNOWN'];
    }
    $statuses = array_values(array_unique($statuses));

    $force = (string)($_POST['force'] ?? ($_POST['noCache'] ?? ($_GET['force'] ?? ($_GET['noCache'] ?? ''))));
    $force = trim($force);
    $forceOn = ($force === '1' || strtolower($force) === 'true' || strtolower($force) === 'yes');

    $cacheDir = __DIR__ . '/cache/list_counts';
    if (!is_dir($cacheDir)) {
        @mkdir($cacheDir, 0755, true);
    }
    $cookieSig = 'nocookie';
    try {
        $cookieSig = substr(sha1((string)$proxy->getCookies()), 0, 12);
    } catch (Throwable) {
        $cookieSig = 'nocookie';
    }
    $cacheKey = sha1($cookieSig . '|' . strtolower((string)$service) . '|' . $officeId . '|' . implode(',', $statuses));
    $cacheFile = $cacheDir . '/counts_' . $cacheKey . '.json';
    $ttl = 10;
    if (!$forceOn) {
        if (is_file($cacheFile) && (time() - (int)@filemtime($cacheFile) < $ttl)) {
            $cached = (string)@file_get_contents($cacheFile);
            if ($cached !== '') {
                http_response_code(200);
                echo $cached;
                exit;
            }
        }
    }

    $counts = [];
    foreach ($statuses as $st) {
        $queryParams = [
            'status' => $st,
            'draw' => 1,
            'order[0][column]' => 1,
            'order[0][dir]' => 'desc',
            'order[0][name]' => '',
            'start' => 0,
            'length' => 1,
            'search[value]' => '',
            'search[regex]' => 'false'
        ];
        if (ctype_digit($officeId)) {
            $queryParams['officeId'] = $officeId;
        } else {
            $queryParams['data'] = $officeId;
        }

        $resp = $proxy->fetch($cfg['list_url'], 'GET', $queryParams, false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
        $body = (string)($resp['body'] ?? '');
        if (($resp['status'] ?? 0) !== 200 || looksHtml($body)) {
            $msg = looksLogin($body)
                ? 'BDRIS সেশন এক্সপায়ার হয়েছে। Reconnect করুন।'
                : (looksBlocked($body) ? 'BDRIS সার্ভার এই কানেকশন ব্লক করছে (360/WAF)। অন্য সেশন দিয়ে চেষ্টা করুন।' : 'BDRIS response invalid');
            sendJson(['ok' => false, 'officeId' => $officeId, 'counts' => $counts, '_error' => $msg], 200);
        }
        $json = json_decode($body, true);
        if (!(json_last_error() === JSON_ERROR_NONE && is_array($json) && isset($json['recordsTotal']))) {
            $counts[$st] = 0;
            continue;
        }
        $c = $json['recordsTotal'];
        $counts[$st] = is_numeric($c) ? (int)$c : (int)preg_replace('/\D+/', '', (string)$c);
    }

    $out = [
        'ok' => true,
        'officeId' => $officeId,
        'statuses' => $statuses,
        'counts' => $counts
    ];
    if (!$forceOn) {
        $encoded = json_encode($out, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if ($encoded !== false) {
            @file_put_contents($cacheFile, $encoded);
        }
    }
    sendJson($out, 200);
}

if ($action === 'details') {
    $encryptedId = trim((string)($_POST['encryptedId'] ?? ($_GET['encryptedId'] ?? '')));
    if ($encryptedId === '') sendJson(['error' => 'encryptedId missing'], 400);
    
    $force = (string)($_POST['force'] ?? ($_POST['noCache'] ?? ($_GET['force'] ?? ($_GET['noCache'] ?? ''))));
    $detailsCacheDir = __DIR__ . '/cache/details';
    if (!is_dir($detailsCacheDir)) {
        @mkdir($detailsCacheDir, 0755, true);
    }
    $detailsCacheFile = $detailsCacheDir . '/' . md5($encryptedId) . '.json';
    
    if ($force === '' && file_exists($detailsCacheFile) && (time() - filemtime($detailsCacheFile) < 300)) {
        $cachedBody = @file_get_contents($detailsCacheFile);
        if ($cachedBody !== '') {
            $json = json_decode($cachedBody, true);
            if (json_last_error() === JSON_ERROR_NONE) {
                sendJson($json, 200);
            }
        }
    }

    $resp = $proxy->fetch($cfg['details_url'], 'GET', ['data' => $encryptedId], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
    $body = (string)($resp['body'] ?? '');
    if (($resp['status'] ?? 0) !== 200 || looksHtml($body)) {
        $msg = looksLogin($body)
            ? 'BDRIS সেশন এক্সপায়ার হয়েছে। Reconnect করুন।'
            : (looksBlocked($body) ? 'BDRIS সার্ভার এই কানেকশন ব্লক করছে (360/WAF)। অন্য সেশন দিয়ে চেষ্টা করুন।' : 'BDRIS response invalid');
        sendJson(['error' => $msg, 'status' => (int)($resp['status'] ?? 0)], 200);
    }
    $json = json_decode($body, true);
    if (json_last_error() === JSON_ERROR_NONE) {
        @file_put_contents($detailsCacheFile, $body);
        sendJson($json, 200);
    }
    sendJson(['error' => 'Invalid JSON'], 200);
}

if ($action === 'possible_duplicates') {
    $encryptedId = trim((string)($_POST['encryptedId'] ?? ($_GET['encryptedId'] ?? '')));
    if ($encryptedId === '') sendJson(['error' => 'encryptedId missing'], 400);
    if (($cfg['kind'] ?? 'reg') !== 'reg' || ($cfg['svc'] ?? 'br') !== 'br') {
        sendJson(['error' => 'Possible Duplicate is supported only for birth applications'], 200);
    }
    $url = 'https://bdris.gov.bd/api/br/all-possible-duplicates-by-birth-registration-application-id';
    $resp = $proxy->fetch($url, 'GET', ['data' => $encryptedId], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
    $body = (string)($resp['body'] ?? '');
    if (($resp['status'] ?? 0) !== 200 || looksHtml($body)) {
        $msg = looksLogin($body)
            ? 'BDRIS সেশন এক্সপায়ার হয়েছে। Reconnect করুন।'
            : (looksBlocked($body) ? 'BDRIS সার্ভার এই কানেকশন ব্লক করছে (360/WAF)। অন্য সেশন দিয়ে চেষ্টা করুন।' : 'BDRIS response invalid');
        sendJson(['error' => $msg, 'status' => (int)($resp['status'] ?? 0)], 200);
    }
    $json = json_decode($body, true);
    if (json_last_error() === JSON_ERROR_NONE) sendJson(['ok' => true, 'data' => $json], 200);
    sendJson(['error' => 'Invalid JSON'], 200);
}

if ($action === 'receive') {
    $encryptedId = trim((string)($_POST['encryptedId'] ?? ($_GET['encryptedId'] ?? '')));
    if ($encryptedId === '') sendJson(['error' => 'encryptedId missing'], 400);
    $url = rtrim($cfg['receive_url'], '/') . '?data=' . urlencode($encryptedId);
    $resp = $proxy->fetch($url, 'POST', [], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
    $body = (string)($resp['body'] ?? '');
    if (($resp['status'] ?? 0) !== 200 || looksHtml($body)) {
        $msg = looksLogin($body)
            ? 'BDRIS সেশন এক্সপায়ার হয়েছে। Reconnect করুন।'
            : (looksBlocked($body) ? 'BDRIS সার্ভার এই কানেকশন ব্লক করছে (360/WAF)। অন্য সেশন দিয়ে চেষ্টা করুন।' : 'BDRIS response invalid');
        sendJson(['error' => $msg, 'status' => (int)($resp['status'] ?? 0)], 200);
    }
    $json = json_decode($body, true);
    if (json_last_error() === JSON_ERROR_NONE) sendJson($json, 200);
    sendJson(['message' => 'OK'], 200);
}

if ($action === 'approve') {
    $encryptedId = trim((string)($_POST['encryptedId'] ?? ($_GET['encryptedId'] ?? '')));
    $status = strtoupper(trim((string)($_POST['status'] ?? ($_GET['status'] ?? ''))));
    if ($encryptedId === '') sendJson(['error' => 'encryptedId missing'], 400);
    $url = null;
    if (($cfg['kind'] ?? 'reg') === 'corr') {
        if ($status === 'REQUIRES_APPROVAL_OF_HIGHER_AUTHORITY' && !empty($cfg['corr_approve_high_url'])) {
            $url = $cfg['corr_approve_high_url'];
        } elseif ($status === 'PENDING_APPROVAL_FOR_NATIONALITY' && !empty($cfg['corr_approve_nat_url'])) {
            $url = $cfg['corr_approve_nat_url'];
        } elseif ($status === 'PENDING_APPROVAL_FOR_DOB' && !empty($cfg['corr_approve_dob_url'])) {
            $url = $cfg['corr_approve_dob_url'];
        }
        if (!$url) {
            sendJson(['error' => 'Approve action is not available for this correction status'], 200);
        }
        $resp = $proxy->fetch($url, 'POST', ['data' => $encryptedId], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
    } elseif (($cfg['kind'] ?? '') === 'cancel') {
        $url = $cfg['approve_url'] ?? null;
        if (!$url) {
            sendJson(['error' => 'Approve action is not available for this cancellation service'], 200);
        }
        $resp = $proxy->fetch($url, 'POST', ['data' => $encryptedId], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
    } else {
        if ($cfg['svc'] !== 'br') {
            sendJson(['error' => 'Approve is supported only for birth applications'], 200);
        }
        if ($status === 'POSSIBLE_DUPLICATE' && $cfg['approve_pd_url']) {
            $url = $cfg['approve_pd_url'];
        } elseif (($status === 'REQUIRES_APPROVAL_WHEN_PARENT_UNKNOWN' || $status === 'PENDING_APPROVAL') && $cfg['approve_wo_parent_url']) {
            $url = $cfg['approve_wo_parent_url'];
        }
        if (!$url) {
            sendJson(['error' => 'Approve action is not available for this status'], 200);
        }
        $resp = $proxy->fetch($url, 'POST', ['data' => $encryptedId], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
    }
    $body = (string)($resp['body'] ?? '');
    if (($resp['status'] ?? 0) !== 200 || looksHtml($body)) {
        $msg = looksLogin($body)
            ? 'BDRIS সেশন এক্সপায়ার হয়েছে। Reconnect করুন।'
            : (looksBlocked($body) ? 'BDRIS সার্ভার এই কানেকশন ব্লক করছে (360/WAF)। অন্য সেশন দিয়ে চেষ্টা করুন।' : 'BDRIS response invalid');
        sendJson(['error' => $msg, 'status' => (int)($resp['status'] ?? 0)], 200);
    }
    $json = json_decode($body, true);
    if (json_last_error() === JSON_ERROR_NONE) sendJson($json, 200);
    sendJson(['message' => 'OK'], 200);
}

if ($action === 'correct') {
    $encryptedId = trim((string)($_POST['encryptedId'] ?? ($_GET['encryptedId'] ?? '')));
    if ($encryptedId === '') sendJson(['error' => 'encryptedId missing'], 400);
    if (($cfg['kind'] ?? 'reg') !== 'corr' || empty($cfg['corr_correct_url'])) {
        sendJson(['error' => 'Correct is supported only for correction applications'], 200);
    }
    $resp = $proxy->fetch($cfg['corr_correct_url'], 'POST', ['data' => $encryptedId], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
    $body = (string)($resp['body'] ?? '');
    if (($resp['status'] ?? 0) !== 200 || looksHtml($body)) {
        $msg = looksLogin($body)
            ? 'BDRIS সেশন এক্সপায়ার হয়েছে। Reconnect করুন।'
            : (looksBlocked($body) ? 'BDRIS সার্ভার এই কানেকশন ব্লক করছে (360/WAF)। অন্য সেশন দিয়ে চেষ্টা করুন।' : 'BDRIS response invalid');
        sendJson(['error' => $msg, 'status' => (int)($resp['status'] ?? 0)], 200);
    }
    $json = json_decode($body, true);
    if (json_last_error() === JSON_ERROR_NONE) sendJson($json, 200);
    sendJson(['message' => 'OK'], 200);
}

if ($action === 'reject') {
    $encryptedId = trim((string)($_POST['encryptedId'] ?? ($_GET['encryptedId'] ?? '')));
    $cause = trim((string)($_POST['causeOfRejection'] ?? ($_GET['causeOfRejection'] ?? '')));
    $causeFree = trim((string)($_POST['causeOfRejections'] ?? ($_GET['causeOfRejections'] ?? '')));
    if ($encryptedId === '') sendJson(['error' => 'encryptedId missing'], 400);
    if ($cause === '') sendJson(['error' => 'causeOfRejection missing'], 200);
    $payload = [
        'data' => $encryptedId,
        'causeOfRejection' => $cause,
        'causeOfRejections' => $causeFree
    ];
    $resp = $proxy->fetch($cfg['reject_url'], 'POST', $payload, false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
    $body = (string)($resp['body'] ?? '');
    if (($resp['status'] ?? 0) !== 200 || looksHtml($body)) {
        $msg = looksLogin($body)
            ? 'BDRIS সেশন এক্সপায়ার হয়েছে। Reconnect করুন।'
            : (looksBlocked($body) ? 'BDRIS সার্ভার এই কানেকশন ব্লক করছে (360/WAF)। অন্য সেশন দিয়ে চেষ্টা করুন।' : 'BDRIS response invalid');
        sendJson(['error' => $msg, 'status' => (int)($resp['status'] ?? 0)], 200);
    }
    $json = json_decode($body, true);
    if (json_last_error() === JSON_ERROR_NONE) sendJson($json, 200);
    sendJson(['message' => 'OK'], 200);
}

if ($action === 'delete') {
    $encryptedId = trim((string)($_POST['encryptedId'] ?? ($_GET['encryptedId'] ?? '')));
    if ($encryptedId === '') sendJson(['error' => 'encryptedId missing'], 400);
    
    $url = 'https://bdris.gov.bd/api/office/application-verifier/delete';
    // BDRIS requires x-www-form-urlencoded or JSON? Typically POST to delete endpoint uses form data or raw payload. Let's send as JSON since $baseHeaders has client: bris and expects JSON.
    // wait, bdris-relay.php usually uses form-urlencoded for simple endpoints, but API endpoints use JSON. proxy_list.php sets $isJson to false (default).
    // Let's pass the payload exactly as BDRIS does
    $payload = ['data' => $encryptedId];
    $resp = $proxy->fetch($url, 'POST', $payload, false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
    $body = (string)($resp['body'] ?? '');
    if (($resp['status'] ?? 0) !== 200 || looksHtml($body)) {
        $msg = looksLogin($body)
            ? 'BDRIS সেশন এক্সপায়ার হয়েছে। Reconnect করুন।'
            : (looksBlocked($body) ? 'BDRIS সার্ভার এই কানেকশন ব্লক করছে (360/WAF)। অন্য সেশন দিয়ে চেষ্টা করুন।' : 'BDRIS response invalid');
        sendJson(['error' => $msg, 'status' => (int)($resp['status'] ?? 0)], 200);
    }
    $json = json_decode($body, true);
    if (json_last_error() === JSON_ERROR_NONE) sendJson($json, 200);
    sendJson(['message' => 'OK'], 200);
}

if ($action === 'register') {
    $encryptedId = trim((string)($_POST['encryptedId'] ?? ($_GET['encryptedId'] ?? '')));
    if ($encryptedId === '') sendJson(['error' => 'encryptedId missing'], 400);

    $payload = ['data' => $encryptedId];
    if ($cfg['svc'] === 'br' && ($cfg['kind'] ?? 'reg') === 'reg') {
        $payload['birthPlaceAndDobVerifierName'] = trim((string)($_POST['birthPlaceAndDobVerifierName'] ?? ''));
        $payload['birthPlaceAndDobVerifierBrn'] = trim((string)($_POST['birthPlaceAndDobVerifierBrn'] ?? ''));
        $payload['birthPlaceAndDobVerificationDate'] = trim((string)($_POST['birthPlaceAndDobVerificationDate'] ?? ''));
        $payload['permAddrVerifierName'] = trim((string)($_POST['permAddrVerifierName'] ?? ''));
        $payload['permAddrVerifierBrn'] = trim((string)($_POST['permAddrVerifierBrn'] ?? ''));
        $payload['permAddrVerificationDate'] = trim((string)($_POST['permAddrVerificationDate'] ?? ''));
        $payload['otp'] = trim((string)($_POST['otp'] ?? ''));

        if ($payload['birthPlaceAndDobVerifierName'] === '' || $payload['birthPlaceAndDobVerifierBrn'] === '' || $payload['birthPlaceAndDobVerificationDate'] === '' || $payload['permAddrVerifierName'] === '' || $payload['permAddrVerifierBrn'] === '' || $payload['permAddrVerificationDate'] === '') {
            sendJson(['error' => 'Verifier info missing'], 200);
        }
    }

    $resp = $proxy->fetch($cfg['register_url'], 'POST', $payload, false, $cfg['referer_search'], $baseHeaders, 30, 2, 1200);
    $body = (string)($resp['body'] ?? '');
    if (($resp['status'] ?? 0) !== 200 || looksHtml($body)) {
        $msg = looksLogin($body)
            ? 'BDRIS সেশন এক্সপায়ার হয়েছে। Reconnect করুন।'
            : (looksBlocked($body) ? 'BDRIS সার্ভার এই কানেকশন ব্লক করছে (360/WAF)। অন্য সেশন দিয়ে চেষ্টা করুন।' : 'BDRIS response invalid');
        sendJson(['error' => $msg, 'status' => (int)($resp['status'] ?? 0)], 200);
    }
    $json = json_decode($body, true);
    if (json_last_error() === JSON_ERROR_NONE) sendJson($json, 200);
    sendJson(['message' => 'OK'], 200);
}
if ($action === 'test_urls') {
    $out = [];
    exec('pdftotext -v 2>&1', $out, $ret);
    sendJson([
        'pdftotext_status' => $ret,
        'pdftotext_output' => $out
    ], 200);
}


if ($action === 'resolve_ubrn') {
    $appIdRaw = (string)($_POST['appId'] ?? ($_GET['appId'] ?? ($_POST['app_id'] ?? ($_GET['app_id'] ?? ''))));
    $appId = preg_replace('/\D+/', '', $appIdRaw);
    if ($appId === '') sendJson(['error' => 'appId missing'], 400);

    $details = null;
    $try1 = $proxy->fetch('https://bdris.gov.bd/api/br/info/application/' . urlencode($appId), 'GET', [], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
    $b1 = (string)($try1['body'] ?? '');
    if (($try1['status'] ?? 0) === 200 && !looksHtml($b1)) {
        $j1 = json_decode($b1, true);
        if (json_last_error() === JSON_ERROR_NONE && is_array($j1)) $details = $j1;
    }
    if (!$details) {
        $try2 = $proxy->fetch($cfg['details_url'], 'GET', ['data' => $appId], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
        $b2 = (string)($try2['body'] ?? '');
        if (($try2['status'] ?? 0) === 200 && !looksHtml($b2)) {
            $j2 = json_decode($b2, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($j2)) $details = $j2;
        }
    }
    if (!$details) {
        $try3 = $proxy->fetch('https://bdris.gov.bd/api/br/application/details/' . urlencode($appId), 'GET', [], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
        $b3 = (string)($try3['body'] ?? '');
        if (($try3['status'] ?? 0) === 200 && !looksHtml($b3)) {
            $j3 = json_decode($b3, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($j3)) $details = $j3;
        }
    }

    $ubrn = '';
    $dob = '';
    if (is_array($details)) {
        $ubrn = (string)($details['birthRegistrationNumber'] ?? ($details['birthRegNo'] ?? ($details['regNo'] ?? ($details['ubrn'] ?? ($details['personBrn'] ?? '')))));
        $dob = (string)($details['personBirthDate'] ?? ($details['dateOfBirth'] ?? ($details['personBirthDateString'] ?? '')));
        if (preg_match('/^(\d{4}-\d{2}-\d{2})T/', $dob, $m)) {
            $dob = $m[1];
        }
    }

    $ubrn = preg_replace('/\D+/', '', $ubrn);
    if ($ubrn === '' || $dob === '') {
        // Fallback: Query BDRIS applications list with search parameter = appId
        $statuses = ['REGISTERED', 'RECEIVED', 'APPLIED', 'POSSIBLE_DUPLICATE', 'REQUIRES_APPROVAL_WHEN_PARENT_UNKNOWN', 'ALL'];
        foreach ($statuses as $st) {
            $queryParams = [
                'status' => $st,
                'draw' => 1,
                'order[0][column]' => 1,
                'order[0][dir]' => 'desc',
                'order[0][name]' => '',
                'start' => 0,
                'length' => 10,
                'search[value]' => $appId,
                'search[regex]' => 'false'
            ];
            $officeIdTry = '';
            $offFile = $proxy->getActiveOfficeFile();
            if ($offFile && file_exists($offFile)) {
                $officeIdTry = trim(file_get_contents($offFile));
            }
            if ($officeIdTry !== '') {
                if (ctype_digit($officeIdTry)) {
                    $queryParams['officeId'] = $officeIdTry;
                } else {
                    $queryParams['data'] = $officeIdTry;
                }
            }
            $resp = $proxy->fetch($cfg['list_url'], 'GET', $queryParams, false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
            $body = (string)($resp['body'] ?? '');
            if (($resp['status'] ?? 0) === 200 && !looksHtml($body)) {
                $json = json_decode($body, true);
                if (json_last_error() === JSON_ERROR_NONE && is_array($json) && isset($json['data']) && is_array($json['data'])) {
                    foreach ($json['data'] as $row) {
                        if (!is_array($row)) continue;
                        $rid = (string)($row['id'] ?? ($row['applicationId'] ?? ($row['appId'] ?? '')));
                        if ($rid === $appId) {
                            $ubrn = (string)($row['birthRegistrationNumber'] ?? ($row['birthRegNo'] ?? ($row['regNo'] ?? ($row['ubrn'] ?? ($row['personBrn'] ?? ($row['brn'] ?? ($row['brn17'] ?? ($row['brn_17'] ?? ''))))))));
                            $dob = (string)($row['personBirthDate'] ?? ($row['dateOfBirth'] ?? ($row['personBirthDateString'] ?? '')));
                            if (preg_match('/^(\d{4}-\d{2}-\d{2})T/', $dob, $m)) {
                                $dob = $m[1];
                            }
                            if ($ubrn !== '' && $dob !== '') {
                                break 2;
                            }
                        }
                    }
                }
            }
        }
    }

    $ubrn = preg_replace('/\D+/', '', $ubrn);
    if ($ubrn === '' || $dob === '') {
        sendJson([
            'error' => 'UBRN/DOB not found',
            'appId' => $appId,
            'details' => $details,
            'debug' => [
                'try1' => ['status' => $try1['status'] ?? null, 'length' => strlen($b1), 'looksHtml' => looksHtml($b1), 'body' => substr(strip_tags($b1), 0, 100)],
                'try2' => ['status' => $try2['status'] ?? null, 'length' => strlen($b2), 'looksHtml' => looksHtml($b2), 'body' => substr(strip_tags($b2), 0, 100)],
                'try3' => ['status' => $try3['status'] ?? null, 'length' => strlen($b3), 'looksHtml' => looksHtml($b3), 'body' => substr(strip_tags($b3), 0, 100)],
            ]
        ], 200);
    }
    sendJson(['appId' => $appId, 'ubrn' => $ubrn, 'dob' => $dob], 200);
}

if ($action === 'print_pdf_cert') {
    $encryptedId = trim((string)($_POST['encryptedId'] ?? ($_POST['encryptedid'] ?? ($_GET['encryptedId'] ?? ($_GET['encryptedid'] ?? '')))));
    if ($encryptedId === '') {
        header('HTTP/1.1 400 Bad Request');
        sendJson(['error' => 'encryptedId missing'], 400);
    }

    $printUrl = 'https://bdris.gov.bd/admin/new-certificate/print?data=' . urlencode($encryptedId);
    $resp = $proxy->fetch($printUrl, 'GET', [], false, 'https://bdris.gov.bd/admin/br/applications/search', $baseHeaders, 30, 2, 1200);
    $body = (string)($resp['body'] ?? '');

    if (looksLogin($body) || looksBlocked($body) || ($resp['status'] ?? 0) !== 200 || strlen($body) < 1000) {
        header('Content-Type: text/html; charset=utf-8');
        echo "<h3>PDF Load Error</h3>";
        if (looksLogin($body)) echo "<p>BDRIS session has expired. Please reconnect.</p>";
        else if (looksBlocked($body)) echo "<p>BDRIS server blocked this connection. Please try again or use another session.</p>";
        else echo "<p>Invalid response from BDRIS server (HTTP " . ($resp['status'] ?? 0) . ")</p>";
        exit;
    }

    header('Content-Type: application/pdf');
    header('Content-Disposition: inline; filename="certificate.pdf"');
    header('Content-Length: ' . strlen($body));
    echo $body;
    exit;
}

if ($action === 'resolve_cert_ubrn') {
    $encryptedId = trim((string)($_POST['encryptedId'] ?? ($_POST['encryptedid'] ?? ($_GET['encryptedId'] ?? ($_GET['encryptedid'] ?? '')))));
    if ($encryptedId === '') sendJson(['error' => 'encryptedId missing'], 400);

    $pdfUrl = 'proxy_list.php?action=print_pdf_cert&encryptedId=' . urlencode($encryptedId);
    sendJson([
        'encryptedId' => $encryptedId,
        'pdf_url' => $pdfUrl
    ], 200);
}

if ($action === 'resolve_encrypted') {

    $appIdRaw = (string)($_POST['appId'] ?? ($_GET['appId'] ?? ($_POST['app_id'] ?? ($_GET['app_id'] ?? ''))));
    $appId = preg_replace('/\D+/', '', $appIdRaw);
    if ($appId === '') sendJson(['error' => 'appId missing'], 400);

    $dobInput = trim((string)($_POST['dob'] ?? ($_GET['dob'] ?? '')));
    if ($dobInput === '') {
        sendJson(['error' => 'dob_missing', 'message' => 'জন্ম তারিখ প্রদান করুন!'], 200);
    }

    $resolvedCacheDir = __DIR__ . '/cache/resolved_ids';
    if (!is_dir($resolvedCacheDir)) {
        @mkdir($resolvedCacheDir, 0755, true);
    }
    
    $enc = '';
    $actualDob = '';

    // 1. Check local JSON cache (which stores {"encryptedId": "...", "dob": "..."})
    $resolvedCacheFile = $resolvedCacheDir . '/' . $appId . '.json';
    if (file_exists($resolvedCacheFile) && (time() - filemtime($resolvedCacheFile) < 86400)) {
        $cData = json_decode(file_get_contents($resolvedCacheFile), true);
        if (is_array($cData) && !empty($cData['encryptedId'])) {
            $enc = $cData['encryptedId'];
            $actualDob = $cData['dob'] ?? '';
        }
    }

    // 2. Check client-side relay cache (from bdris-relay.php)
    if ($enc === '') {
        $relayCacheFile = __DIR__ . '/cache/relay_' . $appId . '.json';
        if (file_exists($relayCacheFile)) {
            $relayData = json_decode(file_get_contents($relayCacheFile), true);
            if (is_array($relayData) && !empty($relayData['data'])) {
                $raw = $relayData['data'];
                if (isset($raw['data']) && is_array($raw['data'])) $raw = $raw['data'];
                $candEnc = $raw['encryptedId'] ?? $raw['encryptedID'] ?? $raw['encryptedApplicationId'] ?? '';
                $candDob = $raw['personBirthDate'] ?? $raw['dateOfBirth'] ?? $raw['personBirthDateString'] ?? '';
                if ($candEnc !== '') {
                    $enc = $candEnc;
                    $actualDob = $candDob;
                }
            }
        }
    }

    // 3. Search globally using server-side session
    if ($enc === '') {
        $officeIdTry = (string)($_POST['officeId'] ?? ($_GET['officeId'] ?? ''));
        if ($officeIdTry === '') {
            $officeTokenFile = $proxy->getActiveOfficeFile();
            if ($officeTokenFile && file_exists($officeTokenFile)) {
                $officeIdTry = trim(file_get_contents($officeTokenFile));
            }
        }

        $details = null;
        $svcPrefix = ($cfg['svc'] ?? 'br') === 'dr' ? 'dr' : 'br';
        $try1 = $proxy->fetch('https://bdris.gov.bd/api/' . $svcPrefix . '/info/application/' . urlencode($appId), 'GET', [], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
        $b1 = (string)($try1['body'] ?? '');
        if (($try1['status'] ?? 0) === 200 && !looksHtml($b1)) {
            $j1 = json_decode($b1, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($j1)) $details = $j1;
        }
        if (!$details) {
            $try2 = $proxy->fetch($cfg['details_url'], 'GET', ['data' => $appId], false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
            $b2 = (string)($try2['body'] ?? '');
            if (($try2['status'] ?? 0) === 200 && !looksHtml($b2)) {
                $j2 = json_decode($b2, true);
                if (json_last_error() === JSON_ERROR_NONE && is_array($j2)) $details = $j2;
            }
        }

        if (is_array($details)) {
            $enc = (string)($details['encryptedId'] ?? ($details['encryptedID'] ?? ($details['encrypted_id'] ?? ($details['data'] ?? ''))));
            $actualDob = (string)($details['personBirthDate'] ?? ($details['dateOfBirth'] ?? ($details['personBirthDateString'] ?? '')));
        }
    }

    // 4. Try global search list API
    if ($enc === '') {
        $statusTry = ['ALL', 'APPLIED', 'RECEIVED', 'POSSIBLE_DUPLICATE', 'REQUIRES_APPROVAL_WHEN_PARENT_UNKNOWN', 'REGISTERED', 'REJECTED'];
        foreach ($statusTry as $st) {
            $queryParams = [
                'status' => $st,
                'draw' => 1,
                'order[0][column]' => 1,
                'order[0][dir]' => 'desc',
                'order[0][name]' => '',
                'start' => 0,
                'length' => 10,
                'search[value]' => $appId,
                'search[regex]' => 'false'
            ];
            if ($officeIdTry !== '') {
                if (ctype_digit($officeIdTry)) {
                    $queryParams['officeId'] = $officeIdTry;
                } else {
                    $queryParams['data'] = $officeIdTry;
                }
            }

            $resp = $proxy->fetch($cfg['list_url'], 'GET', $queryParams, false, $cfg['referer_search'], $baseHeaders, 25, 2, 1200);
            $body = (string)($resp['body'] ?? '');
            if (($resp['status'] ?? 0) !== 200 || looksHtml($body)) {
                continue;
            }
            $json = json_decode($body, true);
            if (!(json_last_error() === JSON_ERROR_NONE && is_array($json) && isset($json['data']) && is_array($json['data']))) {
                continue;
            }
            foreach ($json['data'] as $row) {
                if (!is_array($row)) continue;
                $rid = (string)($row['id'] ?? ($row['applicationNo'] ?? ($row['appId'] ?? '')));
                if ($rid !== $appId) continue;
                $cand = (string)($row['encryptedId'] ?? ($row['encryptedID'] ?? ($row['encrypted_id'] ?? '')));
                if ($cand !== '') {
                    $enc = $cand;
                    $actualDob = $row['personBirthDate'] ?? ($row['dateOfBirth'] ?? ($row['personBirthDateString'] ?? ''));
                    break 2;
                }
            }
        }
    }

    if ($enc !== '') {
        $normActual = normalizeDateString($actualDob);
        $normUser = normalizeDateString($dobInput);
        if ($normActual !== '' && $normUser !== '' && $normActual === $normUser) {
            // DOB match: Save to local JSON cache
            $cacheData = [
                'encryptedId' => $enc,
                'dob' => $actualDob
            ];
            @file_put_contents($resolvedCacheFile, json_encode($cacheData));
            
            // Delete legacy simple cache file if exists
            $legacyFile = $resolvedCacheDir . '/' . $appId . '.txt';
            if (file_exists($legacyFile)) @unlink($legacyFile);

            sendJson(['appId' => $appId, 'encryptedId' => $enc], 200);
        } else {
            sendJson([
                'error' => 'dob_mismatch',
                'message' => 'দুঃখিত, জন্ম তারিখ মেলেনি!',
                'debug' => ['actual' => $normActual, 'user' => $normUser]
            ], 200);
        }
    }

    sendJson([
        'error' => 'not_found',
        'message' => 'আবেদনটি BDRIS সার্ভারে খুঁজে পাওয়া যায়নি!',
        'relay_url' => 'bdris-relay.php?type=app_id&key=' . urlencode($appId)
    ], 200);

}

$officeId = (string)($_POST['officeId'] ?? ($_GET['officeId'] ?? ''));
if ($officeId === '') {
    sendJson(['draw' => (int)($_POST['draw'] ?? 1), 'recordsTotal' => 0, 'recordsFiltered' => 0, 'data' => [], '_error' => 'Office ID missing'], 200);
}

$draw = isset($_POST['draw']) ? (int)$_POST['draw'] : (isset($_GET['draw']) ? (int)$_GET['draw'] : 1);
$start = isset($_POST['start']) ? (int)$_POST['start'] : (isset($_GET['start']) ? (int)$_GET['start'] : 0);
$length = isset($_POST['length']) ? (int)$_POST['length'] : (isset($_GET['length']) ? (int)$_GET['length'] : 10);
$status = (string)($_POST['status'] ?? ($_GET['status'] ?? 'ALL'));
$orderCol = isset($_POST['orderCol']) ? (int)$_POST['orderCol'] : 1;
$orderDir = strtolower((string)($_POST['orderDir'] ?? 'desc')) === 'asc' ? 'asc' : 'desc';
$searchValue = (string)($_POST['searchValue'] ?? '');

$parseDateApplied = static function ($v): int {
    $s = trim((string)$v);
    if ($s === '') return 0;
    if (preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})/', $s, $m)) {
        return ((int)$m[1]) * 10000 + ((int)$m[2]) * 100 + ((int)$m[3]);
    }
    if (preg_match('/^(\d{1,2})-(\d{1,2})-(\d{4})/', $s, $m)) {
        return ((int)$m[3]) * 10000 + ((int)$m[2]) * 100 + ((int)$m[1]);
    }
    $ts = strtotime($s);
    if ($ts === false) return 0;
    return (int)gmdate('Ymd', $ts);
};

$normalizeStatus = static function ($v): string {
    $st = strtoupper(trim((string)$v));
    $st = preg_replace('/[^A-Z_]/', '', $st);
    return $st;
};

$status = $normalizeStatus($status);

$queryParams = [
    'status' => $status,
    'draw' => $draw,
    'order[0][column]' => $orderCol,
    'order[0][dir]' => $orderDir,
    'order[0][name]' => '',
    'start' => $start,
    'length' => $length,
    'search[value]' => $searchValue,
    'search[regex]' => 'false'
];
if (ctype_digit($officeId)) {
    $queryParams['officeId'] = $officeId;
} else {
    $queryParams['data'] = $officeId;
}

$allStatuses = [];
if ($status === 'ALL') {
    if (($cfg['kind'] ?? '') === 'corr') {
        $allStatuses = [
            'APPLIED',
            'RECEIVED',
            'REQUIRES_APPROVAL_OF_REMOTE_OFFICE',
            'REQUIRES_APPROVAL_OF_HIGHER_AUTHORITY',
            'PENDING_APPROVAL_FOR_NATIONALITY',
            'PENDING_APPROVAL_FOR_DOB',
            'CORRECTED',
            'REJECTED'
        ];
    } elseif (($cfg['kind'] ?? '') === 'cancel') {
        $allStatuses = ['APPLIED', 'RECEIVED', 'REGISTERED', 'REJECTED'];
    } else {
        if (($cfg['svc'] ?? '') === 'dr') {
            $allStatuses = ['APPLIED', 'RECEIVED', 'REGISTERED', 'REJECTED'];
        } else {
            $allStatuses = ['APPLIED', 'RECEIVED', 'REGISTERED', 'REJECTED', 'POSSIBLE_DUPLICATE', 'REQUIRES_APPROVAL_WHEN_PARENT_UNKNOWN'];
        }
    }
}

if ($status === 'ALL') {
    $need = max(10, $start + max(1, $length));
    $perStatusLen = max(10, min(500, $need));

    $merged = [];
    $seen = [];
    $sumTotal = 0;
    $sumFiltered = 0;
    $firstError = '';
    $firstStatus = 0;

    foreach ($allStatuses as $st) {
        $queryParamsOne = $queryParams;
        $queryParamsOne['status'] = $st;
        $queryParamsOne['start'] = 0;
        $queryParamsOne['length'] = $perStatusLen;

        $respOne = $proxy->fetch($cfg['list_url'], 'GET', $queryParamsOne, false, $cfg['referer_search'], $baseHeaders, 30, 2, 1200);
        $bodyOne = (string)($respOne['body'] ?? '');

        if (($respOne['status'] ?? 0) !== 200 || looksHtml($bodyOne)) {
            if ($firstError === '') {
                $firstError = looksLogin($bodyOne)
                    ? 'BDRIS সেশন এক্সপায়ার হয়েছে। Reconnect করুন।'
                    : (looksBlocked($bodyOne) ? 'BDRIS সার্ভার এই কানেকশন ব্লক করছে (360/WAF)। অন্য সেশন দিয়ে চেষ্টা করুন।' : 'BDRIS response invalid');
                $firstStatus = (int)($respOne['status'] ?? 0);
            }
            continue;
        }

        $jsonOne = json_decode($bodyOne, true);
        if (!(json_last_error() === JSON_ERROR_NONE && is_array($jsonOne))) {
            if ($firstError === '') {
                $firstError = 'Invalid JSON';
                $firstStatus = (int)($respOne['status'] ?? 0);
            }
            continue;
        }

        $totalOne = $jsonOne['recordsTotal'] ?? null;
        $filteredOne = $jsonOne['recordsFiltered'] ?? null;
        if ($totalOne !== null && $totalOne !== '') $sumTotal += (int)$totalOne;
        if ($filteredOne !== null && $filteredOne !== '') $sumFiltered += (int)$filteredOne;

        $dataOne = $jsonOne['data'] ?? [];
        if (!is_array($dataOne)) continue;
        foreach ($dataOne as $row) {
            if (!is_array($row)) continue;
            $rid = (string)($row['encryptedId'] ?? ($row['encryptedID'] ?? ($row['encrypted_id'] ?? '')));
            if ($rid === '') {
                $rid = (string)($row['id'] ?? ($row['applicationId'] ?? ($row['appId'] ?? '')));
            }
            if ($rid === '') continue;
            if (isset($seen[$rid])) continue;
            $seen[$rid] = 1;
            $merged[] = $row;
        }
    }

    if (empty($merged)) {
        sendJson([
            'draw' => $draw,
            'recordsTotal' => 0,
            'recordsFiltered' => 0,
            'data' => [],
            '_error' => $firstError !== '' ? $firstError : 'No data',
            'status' => $firstStatus
        ], 200);
    }

    usort($merged, static function ($a, $b) use ($parseDateApplied, $orderDir) {
        $da = $parseDateApplied($a['dateApplied'] ?? '');
        $db = $parseDateApplied($b['dateApplied'] ?? '');
        if ($da === $db) {
            $ia = (string)($a['id'] ?? ($a['applicationId'] ?? ($a['appId'] ?? '')));
            $ib = (string)($b['id'] ?? ($b['applicationId'] ?? ($b['appId'] ?? '')));
            if ($ia === $ib) return 0;
            return ($orderDir === 'asc') ? strcmp($ia, $ib) : strcmp($ib, $ia);
        }
        return ($orderDir === 'asc') ? ($da <=> $db) : ($db <=> $da);
    });

    $slice = array_slice($merged, max(0, $start), max(0, $length));
    $outTotal = $sumTotal > 0 ? $sumTotal : count($merged);
    $outFiltered = $sumFiltered > 0 ? $sumFiltered : count($merged);
    sendJson([
        'draw' => $draw,
        'recordsTotal' => $outTotal,
        'recordsFiltered' => $outFiltered,
        'data' => $slice
    ], 200);
}

$resp = $proxy->fetch($cfg['list_url'], 'GET', $queryParams, false, $cfg['referer_search'], $baseHeaders, 30, 2, 1200);
$body = (string)($resp['body'] ?? '');

if (($resp['status'] ?? 0) !== 200 || looksHtml($body)) {
    $msg = looksLogin($body)
        ? 'BDRIS সেশন এক্সপায়ার হয়েছে। Reconnect করুন।'
        : (looksBlocked($body) ? 'BDRIS সার্ভার এই কানেকশন ব্লক করছে (360/WAF)। অন্য সেশন দিয়ে চেষ্টা করুন।' : 'BDRIS response invalid');
    sendJson([
        'draw' => $draw,
        'recordsTotal' => 0,
        'recordsFiltered' => 0,
        'data' => [],
        '_error' => $msg,
        'status' => (int)($resp['status'] ?? 0)
    ], 200);
}

$json = json_decode($body, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($json)) {
    $json['draw'] = $draw;
    sendJson($json, (int)($resp['status'] ?? 200));
}

sendJson([
    'draw' => $draw,
    'recordsTotal' => 0,
    'recordsFiltered' => 0,
    'data' => [],
    '_error' => 'Invalid JSON'
], 200);
