<?php
// api/api_marking.php
declare(strict_types=1);

require_once __DIR__ . '/../db.php';
require_once __DIR__ . '/../auth_stub.php';

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

/**
 * ASL BELGISI OPEN API konfiguratsiyasi (doc/aggregation)
 */

// ASL token / server / TIN / BUSINESS PLACE ID
$API_TOKEN          = '4e0d9f49-0eec-4a05-93ec-45eed1f3cc62';
$TIN                = '204083198';
$SERVER             = 'https://xtrace.aslbelgisi.uz';
$BUSINESS_PLACE_ID  = 17800;

// SSCC uchun company prefix (10 xonali)
const SSCC_COMPANY_PREFIX = '5478008327'; // 10 xonali

// XTrace doc storage (siz aytgan)
const XTRACE_DOC_STATUS_PATH = '/public/api/v1/doc/storage/docs/';

// Qo‘shimcha ehtimoliy endpointlar (404 bo‘lsa navbat bilan sinaydi)
const XTRACE_DOC_STATUS_FALLBACK_PATHS = [
    '/public/api/v1/doc/storage/docs/',            // 1) asosiy
    '/public/api/v1/doc/storage/docs/status/',     // 2) status uchun ehtimoliy
    '/public/api/v1/doc/storage/docs/info/',       // 3) info uchun ehtimoliy
    '/public/api/v1/doc/',                         // 4) doc root ehtimoliy
];

// PDF/FILE ehtimoliy yo‘llar (frontend navbat bilan sinaydi)
const XTRACE_DOC_PDF_FALLBACK_PATHS = [
    '/public/api/v1/doc/storage/docs/',        // ba’zan shu o‘zi file beradi
    '/public/api/v1/doc/storage/docs/pdf/',    // ehtimoliy
    '/public/api/v1/doc/storage/docs/file/',   // ehtimoliy
];

/** @var PDO $pdo  db.php ichida keladi */
if (!isset($pdo)) {
    http_response_code(500);
    echo json_encode(['status' => 'error', 'message' => 'DB ulanishi yo‘q'], JSON_UNESCAPED_UNICODE);
    exit;
}

$userId   = current_user_id() ?: 0;
$userName = current_user_name() ?: 'Operator';

/** JSON helper */
function json_ok(array $data = []): void {
    echo json_encode(array_merge(['status' => 'ok'], $data), JSON_UNESCAPED_UNICODE);
    exit;
}
function json_error(string $message, array $extra = []): void {
    echo json_encode(array_merge(['status' => 'error', 'message' => $message], $extra), JSON_UNESCAPED_UNICODE);
    exit;
}

/** Session helper: bitta product uchun blok sessiyasi */
function get_block_session(int $productId): array {
    if (!isset($_SESSION['marking'][$productId])) {
        $_SESSION['marking'][$productId] = [
            'from_id'      => null,
            'until_id'     => null,
            'count'        => 0,
            'waiting_code' => false, // guruh GS1 kutilyapti
            'category_id'  => null,
        ];
    }
    return $_SESSION['marking'][$productId];
}
function set_block_session(int $productId, array $data): void {
    $_SESSION['marking'][$productId] = $data;
}
function reset_block_session(int $productId): void {
    if (isset($_SESSION['marking'][$productId])) {
        unset($_SESSION['marking'][$productId]);
    }
}

/** SSCC check digit hisoblash (mod 10) */
function calculateCheckDigit(string $digits): int {
    $sum = 0;
    $len = strlen($digits);
    for ($i = $len - 1, $pos = 1; $i >= 0; $i--, $pos++) {
        $n = (int)$digits[$i];
        $sum += ($pos % 2 === 1) ? $n * 3 : $n;
    }
    $mod = $sum % 10;
    return $mod === 0 ? 0 : 10 - $mod;
}

/** SSCC-18 kod generatsiyasi (AI 00 bilan) */
function generate_sscc_code(int $blockId): string
{
    if ($blockId < 1) {
        throw new InvalidArgumentException('blockId 1 dan katta bo‘lishi kerak');
    }

    global $pdo; // db.php dan kelgan PDO

    $rawPrefix   = SSCC_COMPANY_PREFIX;    // '5478008327'
    $baseExt     = (int)$rawPrefix[0];     // 5
    $companyBody = substr($rawPrefix, 1);  // '478008327' (9 raqam)

    $SERIAL_PER_EXT = 9999999;             // har bir extension uchun maksimal serial
    $MAX_ATTEMPTS   = 20;                  // dublikat bo‘lsa 20 marta urinib ko‘ramiz

    // 20 marta urinish: dublikat chiqqanda indexni bitta-bitta oshirib boramiz
    for ($attempt = 0; $attempt < $MAX_ATTEMPTS; $attempt++) {

        // 0-based index
        $index     = ($blockId - 1) + $attempt;
        $extOffset = intdiv($index, $SERIAL_PER_EXT); // qaysi "turn": 0,1,2...

        // extension digit = 5,6,7,8,9 ...
        $extDigit = $baseExt + $extOffset;

        if ($extDigit > 9) {
            // 5..9 oralig‘idan chiqib ketmasligi uchun chek
            throw new RuntimeException(
                'SSCC extension diapazoni tugadi: ext=' . $extDigit . ', blockId=' . $blockId
            );
        }

        // Har bir extension uchun serial 1..9 999 999 oralig‘ida
        $serialNumeric = ($index % $SERIAL_PER_EXT) + 1;                // 1..9999999
        $serial        = str_pad((string)$serialNumeric, 7, '0', STR_PAD_LEFT);

        // 1 (ext) + 9 (company) + 7 (serial) = 17 raqam
        $body  = $extDigit . $companyBody . $serial;
        $check = calculateCheckDigit($body);
        $full  = $body . $check; // 18 raqam

        $sscc = '00' . $full;    // AI (00) bilan 20 belgi

        // === DB bo'yicha dublikatni tekshiramiz ===
        $stmt = $pdo->prepare("
            SELECT COUNT(*)
            FROM blocks
            WHERE gtin2_number = :sscc
            LIMIT 1
        ");
        $stmt->execute([':sscc' => $sscc]);
        $cnt = (int)$stmt->fetchColumn();

        if ($cnt === 0) {
            return $sscc;
        }
    }

    throw new RuntimeException('SSCC kodini generatsiya qilib bo‘lmadi (ko‘p dublikat).');
}

/**
 * Toshkent bo‘yicha bugun oralig‘i: today 00:00:00 -> tomorrow 00:00:00
 */
function tz_today_range_tashkent(): array {
    $tz = new DateTimeZone('Asia/Tashkent');
    $start = new DateTime('today', $tz);
    $end   = new DateTime('tomorrow', $tz);
    return [
        'start' => $start->format('Y-m-d H:i:s'),
        'end'   => $end->format('Y-m-d H:i:s'),
    ];
}

/**
 * Statistika + oxirgi bloklar + oxirgi kodlar
 */
function get_status_for_product(PDO $pdo, int $productId, int $userId): array {
    // Mahsulotni olib kelamiz
    $stmt = $pdo->prepare("
        SELECT id, category_id, product_block_unit, generate_type, gtin_number, gtin2_number, name
        FROM products
        WHERE id = :id AND is_active = 1
    ");
    $stmt->execute([':id' => $productId]);
    $product = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$product) {
        return [
            'stats'       => ['current_count' => 0, 'remaining' => 0, 'today_blocks' => 0],
            'last_blocks' => [],
            'last_codes'  => [],
        ];
    }

    $blockUnit = (int)($product['product_block_unit'] ?? 0);

    // Sessiondan open blok
    $sess = get_block_session($productId);
    $currentCount = (int)($sess['count'] ?? 0);
    $remaining    = $blockUnit > 0 ? max(0, $blockUnit - $currentCount) : 0;

    // Bugungi bloklar soni (Toshkent bo‘yicha)
    $r = tz_today_range_tashkent();
    $stmt = $pdo->prepare("
        SELECT COUNT(*) FROM blocks
        WHERE product_id = :pid
          AND created_at >= :start
          AND created_at <  :end
    ");
    $stmt->execute([
        ':pid'   => $productId,
        ':start' => $r['start'],
        ':end'   => $r['end'],
    ]);
    $todayBlocks = (int)$stmt->fetchColumn();

    // Oxirgi 10 ta blok
    $stmt = $pdo->prepare("
        SELECT b.id,
               b.product_id,
               b.category_id,
               b.from_id,
               b.until_id,
               b.gtin2_number,
               b.mode_type,
               b.user_id,
               b.report_id,
               b.created_at,
               p.name  AS product_name,
               u.name  AS user_name
        FROM blocks b
        JOIN products p ON p.id = b.product_id
        LEFT JOIN users u ON u.id = b.user_id
        ORDER BY b.id DESC
        LIMIT 10
    ");
    $stmt->execute();
    $lastBlocks = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // Oxirgi 5 ta kod
    $stmt = $pdo->prepare("
        SELECT c.id,
               c.product_id,
               c.category_id,
               c.gs1_text,
               c.user_id,
               c.created_at,
               p.name AS product_name,
               u.name AS user_name
        FROM codes c
        JOIN products p ON p.id = c.product_id
        LEFT JOIN users u ON u.id = c.user_id
        WHERE c.product_id = :pid
        ORDER BY c.id DESC
        LIMIT 5
    ");
    $stmt->execute([':pid' => $productId]);
    $lastCodes = $stmt->fetchAll(PDO::FETCH_ASSOC);

    return [
        'stats'       => [
            'current_count' => $currentCount,
            'remaining'     => $remaining,
            'today_blocks'  => $todayBlocks,
        ],
        'last_blocks' => $lastBlocks,
        'last_codes'  => $lastCodes,
    ];
}

/**
 * ASL BELGISI javobidan foydalanuvchiga ko‘rsatish uchun qisqa xato matnini yasaydi
 */
function asl_build_error_text(?array $apiResp): string
{
    if (!$apiResp) {
        return '';
    }

    // 1) globalErrors bo‘lsa – u yerdan olamiz
    if (isset($apiResp['globalErrors']) && is_array($apiResp['globalErrors'])) {
        $parts = [];
        foreach ($apiResp['globalErrors'] as $err) {
            $p = [];
            if (!empty($err['errorCode'])) {
                $p[] = 'Kod: ' . $err['errorCode'];
            }
            if (!empty($err['error'])) {
                $p[] = $err['error'];
            }
            if (isset($err['context']['description']) && $err['context']['description'] !== '') {
                $p[] = $err['context']['description'];
            }
            if (!empty($p)) {
                $parts[] = implode(' — ', $p);
            }
        }
        if (!empty($parts)) {
            return implode(' | ', $parts);
        }
    }

    // 2) context bo‘lsa (masalan invalid-input-parameter)
    if (isset($apiResp['context']) && is_array($apiResp['context'])) {
        $ctx = $apiResp['context'];
        $msgParts = [];
        if (!empty($ctx['_message'])) {
            $msgParts[] = $ctx['_message'];
        }
        if (!empty($ctx['description'])) {
            $msgParts[] = $ctx['description'];
        }
        if (!empty($ctx['specificError'])) {
            $msgParts[] = $ctx['specificError'];
        }
        if (!empty($msgParts)) {
            return implode(' | ', $msgParts);
        }
    }

    // 3) oddiy code + errorId bo‘lsa
    $p = [];
    if (!empty($apiResp['code'])) {
        $p[] = 'Kodni identifikatori: ' . $apiResp['code'];
    }
    if (!empty($apiResp['errorId'])) {
        $p[] = 'errorId: ' . $apiResp['errorId'];
    }

    return implode(' | ', $p);
}

/**
 * HTTP kod + ASL javobiga qarab, foydalanuvchiga ko‘rinadigan o‘zbekcha xabar
 */
function asl_map_error_message(int $httpCode, ?array $apiResp): string
{
    $raw = asl_build_error_text($apiResp);
    $errorId = $apiResp['errorId'] ?? null;
    $suffix = $errorId ? " (ASL errorId: {$errorId})" : '';

    if ($httpCode === 401) {
        return "ASL BELGISI bilan ulanishda avtorizatsiya xatosi. API kalit (token) yaroqsiz yoki muddati tugagan. Yangi token o‘rnatib qayta urinib ko‘ring." . $suffix;
    }

    if ($httpCode === 403) {
        return "ASL BELGISI sizga bu amaliyotni bajarishga ruxsat bermadi (403). Loyiha profili va huquqlarni tekshiring." . $suffix;
    }

    if ($httpCode === 404) {
        return "ASL BELGISI tomonda kerakli obyekt topilmadi (404). Kodlar yoki hujjat parametrlari noto‘g‘ri bo‘lishi mumkin." . $suffix;
    }

    if ($httpCode === 429) {
        return "ASL BELGISI serveriga juda tez-tez so‘rov yuborildi (429). Bir oz kutib, qayta urinib ko‘ring." . $suffix;
    }

    if ($httpCode >= 500) {
        return "ASL BELGISI serverida ichki xato yuz berdi. Birozdan so‘ng yana urinib ko‘ring." . $suffix;
    }

    if ($httpCode === 400) {
        if (isset($apiResp['code']) && $apiResp['code'] === 'invalid-input-parameter') {
            $msg = $apiResp['context']['_message'] ?? '';
            if (strpos($msg, 'Duplicates found') !== false) {
                return "Blok ichida takrorlangan (dublikat) kodlar bor. Iltimos, 'Restart' tugmasini bosib, blokni qayta boshlab, kodlarni qaytadan skaner qiling." . $suffix;
            }
        }

        if ($raw !== '') {
            return "ASL BELGISI yuborilgan maʼlumotlarda xatolik topdi: {$raw}." . $suffix;
        }
        return "ASL BELGISI yuborilgan maʼlumotlarda xatolik topdi (400). Kodlarni va blok kodini qayta tekshirib ko‘ring." . $suffix;
    }

    if ($raw !== '') {
        return "ASL BELGISI bilan ishlashda nomaʼlum xato (HTTP {$httpCode}). Tafsilot: {$raw}." . $suffix;
    }

    return "ASL BELGISI bilan ishlashda nomaʼlum xato (HTTP {$httpCode})." . $suffix;
}

/**
 * ASL BELGISI doc/aggregation ga blokni yuborish
 * $isManual = true  => user kiritgan guruh GS1 (oxiridan 6 belgini kesib yuborib yuboramiz)
 * $isManual = false => auto SSCC kod, to'liq yuboriladi
 */
function call_oms_aggregation(
    PDO $pdo,
    int $productId,
    int $userId,
    int $fromId,
    int $untilId,
    string $fullGroupCode,
    int $blockUnit,
    bool $isManual
): array {
    global $API_TOKEN, $SERVER, $BUSINESS_PLACE_ID;

    $fullGroupCode = trim($fullGroupCode);

    if ($fullGroupCode === '') {
        return ['ok' => false, 'message' => "Guruh kodi bo'sh"];
    }

    // --- Guruh kodni tekshirish ---
    if ($isManual) {
        if (strlen($fullGroupCode) <= 6) {
            return ['ok' => false, 'message' => "Guruh kodi juda qisqa"];
        }
        $groupNoticeCode = substr($fullGroupCode, 0, -6);
        $strlen          = strlen($groupNoticeCode);

        $search1 = '0124780083270';
        $search2 = '014780083270';
        $search3 = '01147800832';

        $found1 = strpos($groupNoticeCode, $search1) !== false;
        $found2 = strpos($groupNoticeCode, $search2) !== false;
        $found3 = strpos($groupNoticeCode, $search3) !== false;

        $lenOk  = in_array($strlen, [31, 32, 33], true);
        if (!(($found1 || $found2 || $found3) && $lenOk)) {
            return ['ok' => false, 'message' => "Yaroqsiz guruh GS1 kod. Iltimos qayta tekshiring."];
        }
    } else {
        $groupNoticeCode = $fullGroupCode;
    }

    // --- Shu blokdagi iste'mol kodlarini yig‘amiz ---
    $stmt = $pdo->prepare("
        SELECT gs1_text
        FROM codes
        WHERE product_id = :pid
          AND user_id    = :uid
          AND id BETWEEN :from_id AND :until_id
        ORDER BY id ASC
    ");
    $stmt->execute([
        ':pid'      => $productId,
        ':uid'      => $userId,
        ':from_id'  => $fromId,
        ':until_id' => $untilId,
    ]);
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    if (!$rows) {
        return ['ok' => false, 'message' => "Kodlar topilmadi (from_id / until_id bo'sh)"];
    }

    $codesRaw = [];
    foreach ($rows as $row) {
        $kod = $row['gs1_text'];

        if (strlen($kod) > 6) {
            $kod = substr($kod, 0, -6);
        }
        $kod = str_replace(["\x1D", "\\u001d"], '', $kod);
        $kod = trim($kod);
        if ($kod !== '') {
            $codesRaw[] = $kod;
        }
    }

    if (empty($codesRaw)) {
        return ['ok' => false, 'message' => "API uchun yuboriladigan kodlar bo'sh"];
    }

    $codes = array_values(array_unique($codesRaw));

    // --- doc/aggregation uchun documentBody ni yig‘amiz ---
    // ✅ Uzbekistan vaqti (Asia/Tashkent) bilan ISO
    $docBody = [
        'aggregationUnits' => [
            [
                'aggregationItemsCount'    => count($codes),
                'aggregationUnitCapacity'  => $blockUnit,
                'codes'                    => $codes,
                'unitSerialNumber'         => $groupNoticeCode,
            ],
        ],
        'businessPlaceId' => $BUSINESS_PLACE_ID,
        'documentDate'    => date('c'), // <-- Toshkent time
    ];

    $payload = [
        'documentBody' => base64_encode(json_encode($docBody, JSON_UNESCAPED_UNICODE)),
        // 'signature' => '...', // keyin e-imzo qo‘shiladi
    ];

    $jsonData = json_encode($payload, JSON_UNESCAPED_UNICODE);

    $apiUrl = rtrim($SERVER, '/') . '/public/api/v1/doc/aggregation';

    // --- CURL orqali ASL BELGISI ga yuboramiz ---
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $apiUrl);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Authorization: Bearer ' . $API_TOKEN,
        'Content-Type: application/json;charset=UTF-8',
        'Accept: application/json',
    ]);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);

    $response  = curl_exec($ch);
    $httpCode  = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curlError = curl_error($ch);
    curl_close($ch);

    if ($response === false) {
        return [
            'ok'      => false,
            'message' => "ASL BELGISI API ga ulanishda xato: " . $curlError,
        ];
    }

    $apiResp = json_decode($response, true);

    // --- Muvaffaqiyatli holat: documentId qaytadi ---
    if ($httpCode === 201 || $httpCode === 200) {
        $documentId = $apiResp['documentId'] ?? null;

        return [
            'ok'        => true,
            'report_id' => $documentId,
            'api_raw'   => $apiResp,
        ];
    }

    // --- Xatolarni ishlash ---
    $humanMessage = asl_map_error_message((int)$httpCode, is_array($apiResp) ? $apiResp : null);

    $errorCodeValues = [];

    if (is_array($apiResp) && isset($apiResp['context']['value']) && is_string($apiResp['context']['value'])) {
        $errorCodeValues[] = trim($apiResp['context']['value']);
    }

    if (is_array($apiResp) && isset($apiResp['globalErrors']) && is_array($apiResp['globalErrors'])) {
        foreach ($apiResp['globalErrors'] as $error) {
            if (isset($error['context']['value']) && $error['context']['value'] !== '') {
                $errorCodeValues[] = trim((string)$error['context']['value']);
            }

            $errStr = (string)($error['error'] ?? '');
            if ($errStr !== '') {
                if (preg_match_all('/\bCode\s+([0-9A-Za-z*\'<>;=+\/]+)/u', $errStr, $m1)) {
                    foreach ($m1[1] as $code) {
                        $errorCodeValues[] = $code;
                    }
                }
                if (preg_match_all('/Duplicate codes\s+([0-9A-Za-z*\'<>;=+\/\s]+)/u', $errStr, $m2)) {
                    $codesFound = preg_split('/\s+/', trim($m2[1][0]));
                    foreach ($codesFound as $code) {
                        if ($code !== '') {
                            $errorCodeValues[] = $code;
                        }
                    }
                }
            }
        }
    }

    $errorCodeValues = array_values(array_unique($errorCodeValues));

    $codesOut = [];
    foreach ($codes as $c) {
        $codesOut[] = [
            'value'   => $c,
            'isError' => in_array($c, $errorCodeValues, true),
        ];
    }

    return [
        'ok'                     => false,
        'message'                => $humanMessage,
        'codes'                  => $codesOut,
        'api_http_code'          => (int)$httpCode,
        'api_error_code'         => is_array($apiResp) ? ($apiResp['code'] ?? null) : null,
        'api_error_id'           => is_array($apiResp) ? ($apiResp['errorId'] ?? null) : null,
        'api_error_message_raw'  => asl_build_error_text(is_array($apiResp) ? $apiResp : null),
        'api_raw'                => $apiResp,
    ];
}

/**
 * XTrace JSON request helper (status tekshiruv)
 */
function xtrace_request_json(string $url, string $token): array {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Authorization: Bearer ' . $token,
        'Accept: application/json',
    ]);
    curl_setopt($ch, CURLOPT_TIMEOUT, 25);

    $resp = curl_exec($ch);
    $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $err  = (string)curl_error($ch);
    curl_close($ch);

    if ($resp === false) {
        return ['ok' => false, 'http' => 0, 'error' => $err, 'body' => null, 'raw' => null];
    }

    $json = json_decode((string)$resp, true);
    return [
        'ok'   => ($code >= 200 && $code < 300),
        'http' => $code,
        'error'=> null,
        'body' => $json,
        'raw'  => (string)$resp,
    ];
}

/**
 * XTrace doc status: bir nechta endpoint sinab ko‘radi
 */
function xtrace_try_doc_status(string $server, string $docId, string $token): array {
    $docId = trim($docId);
    if ($docId === '') return ['ok' => false, 'message' => 'document_id bo‘sh'];

    $server = rtrim($server, '/');

    $paths = array_merge([XTRACE_DOC_STATUS_PATH], XTRACE_DOC_STATUS_FALLBACK_PATHS);

    $attempts = [];
    foreach ($paths as $p) {
        $url = $server . rtrim($p, '/') . '/' . rawurlencode($docId);

        $res = xtrace_request_json($url, $token);

        $attempts[] = [
            'url'  => $url,
            'http' => $res['http'] ?? null,
        ];

        if (($res['ok'] ?? false) === true) {
            return [
                'ok'       => true,
                'status'   => $res['body'],
                'endpoint' => $url,
                'attempts' => $attempts,
                'raw'      => $res['raw'] ?? null,
            ];
        }

        if (in_array((int)($res['http'] ?? 0), [401, 403], true)) {
            return [
                'ok'       => false,
                'message'  => 'XTrace avtorizatsiya/ruxsat xatosi ('.$res['http'].')',
                'attempts' => $attempts,
                'raw'      => $res['body'] ?? null,
            ];
        }
    }

    return [
        'ok'       => false,
        'message'  => 'XTrace doc status topilmadi (hamma endpoint 404/hatolik).',
        'attempts' => $attempts,
    ];
}

/**
 * PDF URL variantlar: frontend navbat bilan ochadi
 */
function xtrace_get_pdf_urls(string $server, string $docId): array {
    $docId = trim($docId);
    if ($docId === '') return ['ok' => false, 'message' => 'document_id bo‘sh'];

    $server = rtrim($server, '/');

    $urls = [];
    foreach (XTRACE_DOC_PDF_FALLBACK_PATHS as $p) {
        $urls[] = $server . rtrim($p, '/') . '/' . rawurlencode($docId);
    }

    return [
        'ok'   => true,
        'urls' => $urls,
    ];
}

// ========== ACTION ROUTING ==========
$action = $_GET['action'] ?? $_POST['action'] ?? '';

try {

    // ---- XTRACE DOC STATUS ----
    if ($action === 'get_doc_status') {
        $documentId = trim((string)($_GET['document_id'] ?? $_POST['document_id'] ?? ''));
        global $API_TOKEN, $SERVER;

        $res = xtrace_try_doc_status($SERVER, $documentId, $API_TOKEN);
        if (!$res['ok']) {
            json_error($res['message'] ?? 'XTrace status xato', [
                'attempts' => $res['attempts'] ?? [],
                'raw'      => $res['raw'] ?? null,
            ]);
        }

        json_ok([
            'document_id' => $documentId,
            'endpoint'    => $res['endpoint'] ?? null,
            'attempts'    => $res['attempts'] ?? [],
            'status'      => $res['status'] ?? null,
            'raw'         => $res['raw'] ?? null,
        ]);
    }

    // ---- XTRACE DOC PDF URLS ----
    if ($action === 'get_doc_pdf') {
        $documentId = trim((string)($_GET['document_id'] ?? $_POST['document_id'] ?? ''));
        global $SERVER;

        $res = xtrace_get_pdf_urls($SERVER, $documentId);
        if (!$res['ok']) {
            json_error($res['message'] ?? 'PDF URL xato');
        }

        json_ok([
            'document_id' => $documentId,
            'urls'        => $res['urls'] ?? [],
        ]);
    }

    // ----------------- GET PRODUCTS -----------------
    if ($action === 'get_products') {
        $categoryId = (int)($_GET['category_id'] ?? 0);
        if ($categoryId <= 0) {
            json_error('Kategoriya ID noto‘g‘ri');
        }

        $stmt = $pdo->prepare("
            SELECT id, name, product_image
            FROM products
            WHERE category_id = :cid AND is_active = 1
            ORDER BY name
        ");
        $stmt->execute([':cid' => $categoryId]);
        $products = $stmt->fetchAll(PDO::FETCH_ASSOC);

        json_ok(['products' => $products]);
    }

    // ----------------- GET STATUS -----------------
    if ($action === 'get_status') {
        $productId = (int)($_GET['product_id'] ?? 0);
        if ($productId <= 0) {
            json_error('Mahsulot ID noto‘g‘ri');
        }

        $data = get_status_for_product($pdo, $productId, $userId);
        json_ok($data);
    }

    // ----------------- SCAN CODE -----------------
    if ($action === 'scan_code') {
        $productId  = (int)($_POST['product_id'] ?? 0);
        $categoryId = (int)($_POST['category_id'] ?? 0);
        $gs1Text    = trim((string)($_POST['gs1_text'] ?? ''));

        if ($categoryId <= 0) {
            json_error('Kategoriya majburiy');
        }
        if ($productId <= 0) {
            json_error('Mahsulot majburiy');
        }
        if ($gs1Text === '') {
            json_error('GS1 kodni kiriting');
        }

        // Mahsulotni tekshiramiz
        $stmt = $pdo->prepare("
            SELECT id, category_id, product_block_unit, gtin_number, gtin2_number, generate_type
            FROM products
            WHERE id = :id AND is_active = 1
        ");
        $stmt->execute([':id' => $productId]);
        $product = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$product) {
            json_error('Mahsulot topilmadi yoki aktiv emas');
        }

        if ((int)$product['category_id'] !== $categoryId) {
            json_error('Kategoriya va mahsulot mos kelmadi');
        }

        $blockUnit    = (int)($product['product_block_unit'] ?? 0);
        $gtinNumber   = trim((string)($product['gtin_number'] ?? ''));
        $generateType = strtolower((string)($product['generate_type'] ?? 'manual'));

        // GTIN bo'yicha filter (iste'mol kod)
        if ($gtinNumber !== '' && strpos($gs1Text, $gtinNumber) === false) {
            json_error('GS1 matnda mahsulot GTIN raqami topilmadi');
        }

        // Dublikat kodni tekshiramiz
        $stmt = $pdo->prepare("SELECT id FROM codes WHERE gs1_text = :t LIMIT 1");
        $stmt->execute([':t' => $gs1Text]);
        if ($stmt->fetchColumn()) {
            json_error('Bu GS1 kod bazada allaqachon mavjud (dublikat)');
        }

        // Session + waiting_group tekshiruv
        $sess = get_block_session($productId);
        if (!empty($sess['waiting_code'])) {
            json_error('Avval oldingi blok uchun guruh GS1 kodini kiriting yoki blokni bekor qiling');
        }

        // Kodni saqlaymiz
        $stmt = $pdo->prepare("
            INSERT INTO codes (product_id, category_id, gs1_text, user_id, created_at)
            VALUES (:pid, :cid, :gs1, :uid, NOW())
        ");
        $stmt->execute([
            ':pid' => $productId,
            ':cid' => $categoryId,
            ':gs1' => $gs1Text,
            ':uid' => $userId,
        ]);
        $codeId = (int)$pdo->lastInsertId();

        // Session yangilash
        if ($sess['from_id'] === null) {
            $sess['from_id'] = $codeId;
        }
        $sess['until_id']    = $codeId;
        $sess['count']       = (int)$sess['count'] + 1;
        $sess['category_id'] = $categoryId;
        set_block_session($productId, $sess);

        // Agar blok unit belgilanmagan bo'lsa – har bir kod oddiy "code_saved"
        if ($blockUnit <= 0) {
            $data = get_status_for_product($pdo, $productId, $userId);
            json_ok(array_merge($data, [
                'mode' => 'code_saved',
            ]));
        }

        // Blok to'ldimi?
        if ($sess['count'] >= $blockUnit) {
            $fromId = (int)$sess['from_id'];
            $untilId = (int)$sess['until_id'];

            if ($generateType === 'manual') {
                // MANUAL: foydalanuvchi guruh GS1 kiritishi kerak
                $sess['waiting_code'] = true;
                set_block_session($productId, $sess);

                $data = get_status_for_product($pdo, $productId, $userId);
                json_ok(array_merge($data, [
                    'mode'            => 'need_group_code',
                    'block_candidate' => [
                        'from_id' => $fromId,
                        'until_id'=> $untilId,
                        'size'    => $blockUnit,
                    ],
                ]));
            } else {
                // AUTO: SSCC18 blok kodi generatsiya + ASL ga yuborish
                $pdo->beginTransaction();
                $stmt = $pdo->prepare("
                    INSERT INTO blocks (product_id, category_id, from_id, until_id, gtin2_number, mode_type, user_id, report_id, created_at)
                    VALUES (:pid, :cid, :from_id, :until_id, '', 'auto', :uid, NULL, NOW())
                ");
                $stmt->execute([
                    ':pid'     => $productId,
                    ':cid'     => $categoryId,
                    ':from_id' => $fromId,
                    ':until_id'=> $untilId,
                    ':uid'     => $userId,
                ]);
                $blockId = (int)$pdo->lastInsertId();
                $pdo->commit();

                // SSCC-18 kod
                $ssccCode = generate_sscc_code($blockId);

                // ASL API ga yuborish
                $oms = call_oms_aggregation($pdo, $productId, $userId, $fromId, $untilId, $ssccCode, $blockUnit, false);

                if (!$oms['ok']) {
                    // Blokni bekor qilamiz
                    $stmt = $pdo->prepare("DELETE FROM blocks WHERE id = :id");
                    $stmt->execute([':id' => $blockId]);

                    $data = get_status_for_product($pdo, $productId, $userId);
                    json_error($oms['message'] ?? 'ASL API xatosi (auto)', array_merge($data, [
                        'mode'  => 'block_api_error_auto',
                        'codes' => $oms['codes'] ?? null,
                        'api'   => [
                            'http' => $oms['api_http_code'] ?? null,
                            'code' => $oms['api_error_code'] ?? null,
                            'errorId' => $oms['api_error_id'] ?? null,
                            'rawMessage' => $oms['api_error_message_raw'] ?? null,
                        ],
                    ]));
                }

                // ASL success => blokni yangilaymiz (gtin2_number + report_id)
                $stmt = $pdo->prepare("
                    UPDATE blocks
                    SET gtin2_number = :g2, report_id = :rid
                    WHERE id = :id
                ");
                $stmt->execute([
                    ':g2'  => $ssccCode,
                    ':rid' => $oms['report_id'] ?? null,
                    ':id'  => $blockId,
                ]);

                // Sessionni tozalaymiz
                reset_block_session($productId);

                $data = get_status_for_product($pdo, $productId, $userId);
                json_ok(array_merge($data, [
                    'mode'       => 'block_completed_auto',
                    'group_code' => $ssccCode,
                    'api'        => [
                        'report_id' => $oms['report_id'] ?? null,
                    ],
                ]));
            }
        }

        // Blok to'lmagan => oddiy "code_saved"
        $data = get_status_for_product($pdo, $productId, $userId);
        json_ok(array_merge($data, [
            'mode' => 'code_saved',
        ]));
    }

    // ----------------- ROLLBACK OPEN BLOCK -----------------
    if ($action === 'rollback_open_block') {
        $productId = (int)($_POST['product_id'] ?? 0);
        if ($productId <= 0) {
            json_error('Mahsulot tanlanmagan');
        }
        $sess = get_block_session($productId);
        $fromId = (int)($sess['from_id'] ?? 0);
        if ($fromId <= 0) {
            json_error('Joriy blok yo‘q yoki allaqachon yakunlangan');
        }

        $stmt = $pdo->prepare("
            DELETE FROM codes
            WHERE product_id = :pid
              AND user_id    = :uid
              AND id >= :from_id
        ");
        $stmt->execute([
            ':pid'     => $productId,
            ':uid'     => $userId,
            ':from_id' => $fromId,
        ]);

        reset_block_session($productId);

        $data = get_status_for_product($pdo, $productId, $userId);
        json_ok(array_merge($data, [
            'message' => 'Joriy blokdagi kodlar bekor qilindi',
        ]));
    }

    // ----------------- SAVE GROUP CODE (MANUAL) -----------------
    if ($action === 'save_group_code') {
        $productId     = (int)($_POST['product_id'] ?? 0);
        $groupGs1Text  = trim((string)($_POST['group_gs1_text'] ?? ''));

        if ($productId <= 0) {
            json_error('Mahsulot tanlanmagan');
        }
        if ($groupGs1Text === '') {
            json_error('Guruh GS1 kodini kiriting');
        }

        $stmt = $pdo->prepare("
            SELECT id, category_id, product_block_unit, gtin2_number, generate_type
            FROM products
            WHERE id = :id AND is_active = 1
        ");
        $stmt->execute([':id' => $productId]);
        $product = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$product) {
            json_error('Mahsulot topilmadi');
        }

        $generateType = strtolower((string)($product['generate_type'] ?? 'manual'));
        if ($generateType !== 'manual') {
            json_error('Bu mahsulot avtomatik rejimda. Guruh kodini qo‘lda kiritish shart emas.');
        }

        $blockUnit = (int)($product['product_block_unit'] ?? 0);

        $sess   = get_block_session($productId);
        $fromId = (int)($sess['from_id'] ?? 0);
        $untilId= (int)($sess['until_id'] ?? 0);
        $count  = (int)($sess['count'] ?? 0);

        if ($fromId <= 0 || $untilId <= 0 || $count <= 0) {
            json_error('Joriy blok ma’lumotlari topilmadi (session bo‘sh)');
        }

        if ($blockUnit > 0 && $count !== $blockUnit) {
            json_error("Joriy blok to‘liq emas. Kiritilgan kodlar soni: $count / $blockUnit");
        }

        $gtin2 = trim((string)($product['gtin2_number'] ?? ''));
        if ($gtin2 !== '' && strpos($groupGs1Text, $gtin2) === false) {
            json_error('Guruh GS1 matnida mahsulot GTIN2 raqami topilmadi');
        }

        $stmt = $pdo->prepare("SELECT id FROM blocks WHERE gtin2_number = :g2 LIMIT 1");
        $stmt->execute([':g2' => $groupGs1Text]);
        if ($stmt->fetchColumn()) {
            json_error('Bu guruh GS1 kodi avvaldan blok jadvalida mavjud');
        }

        $oms = call_oms_aggregation(
            $pdo,
            $productId,
            $userId,
            $fromId,
            $untilId,
            $groupGs1Text,
            $blockUnit,
            true
        );

        if (!$oms['ok']) {
            $data = get_status_for_product($pdo, $productId, $userId);
            json_error($oms['message'] ?? 'ASL API xatosi (manual)', array_merge($data, [
                'codes' => $oms['codes'] ?? null,
                'api'   => [
                    'http' => $oms['api_http_code'] ?? null,
                    'code' => $oms['api_error_code'] ?? null,
                    'errorId' => $oms['api_error_id'] ?? null,
                    'rawMessage' => $oms['api_error_message_raw'] ?? null,
                ],
            ]));
        }

        $stmt = $pdo->prepare("
            INSERT INTO blocks (product_id, category_id, from_id, until_id, gtin2_number, mode_type, user_id, report_id, created_at)
            VALUES (:pid, :cid, :from_id, :until_id, :gtin2, 'manual', :uid, :report_id, NOW())
        ");
        $stmt->execute([
            ':pid'       => $productId,
            ':cid'       => (int)$product['category_id'],
            ':from_id'   => $fromId,
            ':until_id'  => $untilId,
            ':gtin2'     => $groupGs1Text,
            ':uid'       => $userId,
            ':report_id' => $oms['report_id'] ?? null,
        ]);

        reset_block_session($productId);

        $data = get_status_for_product($pdo, $productId, $userId);
        json_ok(array_merge($data, [
            'mode'    => 'block_completed_manual',
            'message' => 'Blok guruh GS1 bilan saqlandi va ASL ga yuborildi',
            'api'     => [
                'report_id' => $oms['report_id'] ?? null,
            ],
        ]));
    }

    json_error("Noto‘g‘ri action");

} catch (Throwable $e) {
    json_error('Server xatosi: ' . $e->getMessage());
}
