<?php

require_once __DIR__ . '/app_runtime.php';

if (!function_exists('ua_output_scope_key')) {
    function ua_output_scope_key(?string $scopeKey = null): string
    {
        $scope = trim((string)$scopeKey);
        if ($scope === '') $scope = ua_install_instance_key();
        $scope = preg_replace('/[^A-Za-z0-9_.:-]+/', '-', $scope) ?? '';
        return substr($scope !== '' ? $scope : ua_install_instance_key(), 0, 160);
    }
}

if (!function_exists('ua_output_agent_scope')) {
    function ua_output_agent_scope(int $agentId): string
    {
        return ua_output_scope_key('agent:' . max(0, $agentId));
    }
}

if (!function_exists('ua_output_device_id')) {
    function ua_output_device_id(?string $deviceId): string
    {
        $value = strtolower(trim((string)$deviceId));
        return preg_match('/^[a-z0-9][a-z0-9_-]{7,63}$/', $value) ? $value : '';
    }
}

if (!function_exists('ua_output_device_scope')) {
    function ua_output_device_scope(string $baseScope, ?string $deviceId): string
    {
        $base = preg_replace('/\:device\:[a-z0-9_-]{8,64}$/i', '', ua_output_scope_key($baseScope)) ?? '';
        $device = ua_output_device_id($deviceId);
        return $device !== '' ? ua_output_scope_key($base . ':device:' . $device) : ua_output_scope_key($base);
    }
}

if (!function_exists('ua_output_scope_is_device_child')) {
    function ua_output_scope_is_device_child(string $baseScope, string $candidate): bool
    {
        $base = ua_output_scope_key($baseScope);
        $scope = ua_output_scope_key($candidate);
        return (bool)preg_match('/^' . preg_quote($base, '/') . '\:device\:[a-z0-9][a-z0-9_-]{7,63}$/i', $scope);
    }
}

if (!function_exists('ua_output_session_actor')) {
    function ua_output_session_actor(mysqli $conn): array
    {
        $adminId = (int)($_SESSION['admin_id'] ?? $_SESSION['admin_user_id'] ?? 0);
        if ($adminId > 0 || !empty($_SESSION['admin_user'])) {
            return ['authorized'=>true, 'type'=>'admin', 'id'=>$adminId, 'scope'=>ua_output_scope_key()];
        }
        foreach ($_SESSION as $key => $value) {
            if (empty($value) || !preg_match('/^agent_portal_([1-9][0-9]*)$/', (string)$key, $match)) continue;
            $agentId = (int)$match[1];
            $result = @$conn->query("SELECT id FROM agents WHERE id={$agentId} AND is_active=1 AND portal_enabled=1 AND archived_at IS NULL LIMIT 1");
            if ($result && $result->num_rows) {
                return ['authorized'=>true, 'type'=>'agent', 'id'=>$agentId, 'scope'=>ua_output_agent_scope($agentId)];
            }
        }
        return ['authorized'=>false, 'type'=>'guest', 'id'=>0, 'scope'=>''];
    }
}

if (!function_exists('ua_output_authorized_scope')) {
    function ua_output_authorized_scope(mysqli $conn, ?string $requestedScope = null): array
    {
        $requestedRaw = trim((string)$requestedScope);
        $actor = [];
        if (preg_match('/^agent\:([1-9][0-9]*)(?:\:device\:|$)/i', $requestedRaw, $agentMatch)) {
            $requestedAgentId = (int)$agentMatch[1];
            if (!empty($_SESSION['agent_portal_' . $requestedAgentId])) {
                $activeAgent = @$conn->query("SELECT id FROM agents WHERE id={$requestedAgentId} AND is_active=1 AND portal_enabled=1 AND archived_at IS NULL LIMIT 1");
                if ($activeAgent && $activeAgent->num_rows) {
                    $actor = ['authorized'=>true, 'type'=>'agent', 'id'=>$requestedAgentId, 'scope'=>ua_output_agent_scope($requestedAgentId)];
                }
            }
        }
        if (!$actor) $actor = ua_output_session_actor($conn);
        if (empty($actor['authorized'])) return $actor;
        $baseScope = ua_output_scope_key((string)$actor['scope']);
        $requested = $requestedRaw;
        if ($requested === '' || ua_output_scope_key($requested) === $baseScope) {
            $actor['scope'] = $baseScope;
            $actor['base_scope'] = $baseScope;
            return $actor;
        }
        if (!ua_output_scope_is_device_child($baseScope, $requested)) {
            return ['authorized'=>false, 'type'=>(string)$actor['type'], 'id'=>(int)$actor['id'], 'scope'=>'', 'base_scope'=>$baseScope];
        }
        $actor['base_scope'] = $baseScope;
        $actor['scope'] = ua_output_scope_key($requested);
        return $actor;
    }
}

if (!function_exists('ua_output_agent_base')) {
    function ua_output_agent_base(string $host): string
    {
        $host = trim($host);
        if ($host === '') $host = '127.0.0.1';
        if (!preg_match('~^https?://~i', $host)) $host = 'http://' . $host;
        $parts = parse_url($host);
        if (!is_array($parts) || empty($parts['host']) || !empty($parts['user']) || !empty($parts['pass'])) {
            return 'http://127.0.0.1:9123';
        }
        $scheme = strtolower((string)($parts['scheme'] ?? 'http')) === 'https' ? 'https' : 'http';
        $port = (int)($parts['port'] ?? 9123);
        if ($port < 1 || $port > 65535) $port = 9123;
        $hostname = trim((string)$parts['host'], '[]');
        if (filter_var($hostname, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) $hostname = '[' . $hostname . ']';
        return $scheme . '://' . $hostname . ':' . $port;
    }
}

if (!function_exists('ua_output_agent_request')) {
    function ua_output_agent_request(
        string $host,
        string $token,
        string $path,
        string $method = 'GET',
        ?string $body = null,
        int $timeout = 12
    ): array {
        if (!function_exists('curl_init')) return ['ok'=>false, 'status'=>0, 'body'=>'', 'data'=>null, 'error'=>'curl_unavailable'];
        $url = ua_output_agent_base($host) . '/' . ltrim($path, '/');
        $headers = ['Accept: application/json', 'Content-Type: application/json'];
        if (trim($token) !== '') $headers[] = 'X-UA-Output-Token: ' . trim($token);
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER=>true,
            CURLOPT_FOLLOWLOCATION=>false,
            CURLOPT_CONNECTTIMEOUT=>3,
            CURLOPT_TIMEOUT=>max(3, min(60, $timeout)),
            CURLOPT_CUSTOMREQUEST=>strtoupper($method),
            CURLOPT_HTTPHEADER=>$headers,
        ]);
        if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        $response = curl_exec($ch);
        $error = curl_error($ch);
        $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        $data = json_decode((string)$response, true);
        return [
            'ok'=>$response !== false && $status >= 200 && $status < 300,
            'status'=>$status,
            'body'=>(string)$response,
            'data'=>is_array($data) ? $data : null,
            'error'=>$error,
            'url'=>$url,
        ];
    }
}

if (!function_exists('ua_output_command_registry')) {
    function ua_output_command_registry(): array
    {
        return [
            'contract_print' => ['type'=>'printer','group'=>'المشتركين والعقود','label'=>'طباعة عقد مشترك','source'=>'subscriber_contracts.php','paper'=>'A4','icon'=>'fa-file-word-o'],
            'subscriber_profile_print' => ['type'=>'printer','group'=>'المشتركين والعقود','label'=>'طباعة معلومات المشترك وأكواد الاتصال','source'=>'subscribers_add.php|subscribers_edit.php|agent_subscriber_add.php|subscriber_info_print.php','paper'=>'A4','icon'=>'fa-id-card-o'],
            'invoice_a4' => ['type'=>'printer','group'=>'الفوترة والمبيعات','label'=>'طباعة فاتورة A4','source'=>'print_invoice.php?paper=A4','paper'=>'A4','icon'=>'fa-file-text-o'],
            'invoice_thermal' => ['type'=>'printer','group'=>'الفوترة والمبيعات','label'=>'طباعة فاتورة حرارية','source'=>'print_invoice.php?paper=80mm|58mm','paper'=>'80mm','icon'=>'fa-calculator'],
            'sales_receipt' => ['type'=>'printer','group'=>'الفوترة والمبيعات','label'=>'طباعة فاتورة مبيعات','source'=>'sales_view.php','paper'=>'80mm','icon'=>'fa-shopping-cart'],
            'voucher_cards' => ['type'=>'printer','group'=>'الكروت والرموز','label'=>'طباعة كروت الإنترنت','source'=>'vouchers_print.php','paper'=>'A4','icon'=>'fa-ticket'],
            'wifi_qr_cards' => ['type'=>'printer','group'=>'الكروت والرموز','label'=>'طباعة بطاقات وQR شبكات Wi-Fi الحرارية','source'=>'subscriber_router_setup.php|android_router_setup.php','paper'=>'80mm','icon'=>'fa-qrcode'],
            'subscriber_connection' => ['type'=>'printer','group'=>'المشتركين والعقود','label'=>'طباعة تعليمات ربط المشترك','source'=>'customer_connect.php','paper'=>'A4','icon'=>'fa-plug'],
            'daily_finance_report' => ['type'=>'printer','group'=>'التقارير','label'=>'طباعة التقرير المالي اليومي','source'=>'finance_day_report.php','paper'=>'A4','icon'=>'fa-calendar-check-o'],
            'monthly_finance_report' => ['type'=>'printer','group'=>'التقارير','label'=>'طباعة التقرير المالي الشهري','source'=>'finance_month_report.php','paper'=>'A4','icon'=>'fa-calendar'],
            'general_report' => ['type'=>'printer','group'=>'التقارير','label'=>'طباعة التقارير العامة','source'=>'print_preview.php','paper'=>'A4','icon'=>'fa-bar-chart'],
            'contract_scan' => ['type'=>'scanner','group'=>'المشتركين والعقود','label'=>'مسح العقد الموقع ضوئياً','source'=>'subscriber_contracts.php','paper'=>'document','icon'=>'fa-file-image-o'],
            'subscriber_identity_scan' => ['type'=>'scanner','group'=>'المشتركين والعقود','label'=>'مسح هوية المشترك ضوئياً','source'=>'subscribers_add.php|subscribers_edit.php','paper'=>'document','icon'=>'fa-id-card-o'],
            'subscriber_attachment_scan' => ['type'=>'scanner','group'=>'المشتركين والعقود','label'=>'مسح مرفقات المشترك ضوئياً','source'=>'subscribers_add.php|subscribers_edit.php','paper'=>'document','icon'=>'fa-paperclip'],
            'purchase_document_scan' => ['type'=>'scanner','group'=>'الفوترة والمبيعات','label'=>'مسح فاتورة أو مستند مشتريات','source'=>'purchases_new.php','paper'=>'document','icon'=>'fa-shopping-bag'],
        ];
    }
}

if (!function_exists('ua_output_ensure_schema')) {
    function ua_output_ensure_schema(mysqli $conn): void
    {
        static $schemaReady = false;
        if ($schemaReady) return;

        @$conn->query("CREATE TABLE IF NOT EXISTS ua_output_routes (
            instance_key VARCHAR(160) NOT NULL,
            command_key VARCHAR(80) NOT NULL,
            device_type ENUM('printer','scanner') NOT NULL,
            device_name VARCHAR(255) NOT NULL DEFAULT '',
            agent_host VARCHAR(180) NOT NULL DEFAULT '127.0.0.1',
            agent_token VARCHAR(160) NOT NULL DEFAULT '',
            paper_size VARCHAR(30) NOT NULL DEFAULT '',
            copies SMALLINT UNSIGNED NOT NULL DEFAULT 1,
            is_enabled TINYINT(1) NOT NULL DEFAULT 1,
            updated_by INT NULL,
            updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (instance_key, command_key),
            KEY idx_output_device (instance_key, device_type, device_name)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

        @$conn->query("CREATE TABLE IF NOT EXISTS ua_output_jobs (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
            instance_key VARCHAR(160) NOT NULL,
            command_key VARCHAR(80) NOT NULL,
            device_type ENUM('printer','scanner') NOT NULL,
            device_name VARCHAR(255) NOT NULL DEFAULT '',
            agent_host VARCHAR(180) NOT NULL DEFAULT '127.0.0.1',
            source_path VARCHAR(255) NOT NULL DEFAULT '',
            reference_id VARCHAR(120) NOT NULL DEFAULT '',
            job_status VARCHAR(30) NOT NULL DEFAULT 'created',
            status_message VARCHAR(500) NOT NULL DEFAULT '',
            admin_id INT NULL,
            actor_type VARCHAR(30) NOT NULL DEFAULT 'admin',
            actor_id INT NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            KEY idx_output_jobs_command (instance_key, command_key, created_at),
            KEY idx_output_jobs_status (job_status, created_at)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

        // بطاقات Wi-Fi صُممت كتذكرة حرارية؛ حوّل الإعدادات القديمة من A4 مرة واحدة.
        @$conn->query("UPDATE ua_output_routes SET paper_size='80mm' WHERE command_key='wifi_qr_cards' AND paper_size NOT IN ('80mm','58mm')");

        $scope = $conn->real_escape_string(ua_install_instance_key());
        $routeColumn = @$conn->query("SHOW COLUMNS FROM ua_output_routes LIKE 'instance_key'");
        if (!$routeColumn || $routeColumn->num_rows === 0) {
            @$conn->query("ALTER TABLE ua_output_routes ADD instance_key VARCHAR(160) NOT NULL DEFAULT '' FIRST");
            @$conn->query("UPDATE ua_output_routes SET instance_key='{$scope}' WHERE instance_key=''");
            @$conn->query("ALTER TABLE ua_output_routes DROP PRIMARY KEY, ADD PRIMARY KEY(instance_key, command_key)");
            @$conn->query("ALTER TABLE ua_output_routes ADD KEY idx_output_instance_device(instance_key, device_type, device_name)");
        } else {
            $routeDefinition = $routeColumn->fetch_assoc();
            if (preg_match('/varchar\((\d+)\)/i', (string)($routeDefinition['Type'] ?? ''), $match) && (int)$match[1] < 160) {
                @$conn->query("ALTER TABLE ua_output_routes MODIFY instance_key VARCHAR(160) NOT NULL");
            }
        }
        $jobColumn = @$conn->query("SHOW COLUMNS FROM ua_output_jobs LIKE 'instance_key'");
        if (!$jobColumn || $jobColumn->num_rows === 0) {
            @$conn->query("ALTER TABLE ua_output_jobs ADD instance_key VARCHAR(160) NOT NULL DEFAULT '' AFTER id");
            @$conn->query("UPDATE ua_output_jobs SET instance_key='{$scope}' WHERE instance_key=''");
            @$conn->query("ALTER TABLE ua_output_jobs ADD KEY idx_output_jobs_instance(instance_key, command_key, created_at)");
        } else {
            $jobDefinition = $jobColumn->fetch_assoc();
            if (preg_match('/varchar\((\d+)\)/i', (string)($jobDefinition['Type'] ?? ''), $match) && (int)$match[1] < 160) {
                @$conn->query("ALTER TABLE ua_output_jobs MODIFY instance_key VARCHAR(160) NOT NULL");
            }
        }
        foreach ([
            'agent_token' => "VARCHAR(160) NOT NULL DEFAULT '' AFTER agent_host",
        ] as $column => $definition) {
            $check = @$conn->query("SHOW COLUMNS FROM ua_output_routes LIKE '{$column}'");
            if (!$check || $check->num_rows === 0) @$conn->query("ALTER TABLE ua_output_routes ADD `{$column}` {$definition}");
        }
        foreach ([
            'agent_host' => "VARCHAR(180) NOT NULL DEFAULT '127.0.0.1' AFTER device_name",
            'actor_type' => "VARCHAR(30) NOT NULL DEFAULT 'admin' AFTER admin_id",
            'actor_id' => "INT NULL AFTER actor_type",
        ] as $column => $definition) {
            $check = @$conn->query("SHOW COLUMNS FROM ua_output_jobs LIKE '{$column}'");
            if (!$check || $check->num_rows === 0) @$conn->query("ALTER TABLE ua_output_jobs ADD `{$column}` {$definition}");
        }
        $schemaReady = true;
    }
}

if (!function_exists('ua_output_run_powershell_json')) {
    function ua_output_run_powershell_json(string $script): array
    {
        if (stripos(PHP_OS_FAMILY, 'Windows') === false) return ['ok'=>false,'error'=>'اكتشاف الأجهزة المباشر متاح على ويندوز فقط.'];
        if (!function_exists('shell_exec') || stripos((string)ini_get('disable_functions'), 'shell_exec') !== false) {
            return ['ok'=>false,'error'=>'تشغيل أوامر اكتشاف ويندوز غير متاح في إعدادات PHP.'];
        }
        $prefix = '[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false);$ErrorActionPreference="Stop";';
        $source = $prefix . $script;
        if (function_exists('mb_convert_encoding')) {
            $wide = mb_convert_encoding($source, 'UTF-16LE', 'UTF-8');
        } else {
            $wide = iconv('UTF-8', 'UTF-16LE', $source);
        }
        if ($wide === false) return ['ok'=>false,'error'=>'تعذر تجهيز أمر اكتشاف ويندوز.'];
        $encoded = base64_encode($wide);
        $raw = @shell_exec('powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ' . $encoded . ' 2>NUL');
        $raw = trim((string)$raw);
        if ($raw === '') return ['ok'=>false,'error'=>'لم يرجع ويندوز بيانات أجهزة.'];
        $decoded = json_decode($raw, true);
        if (!is_array($decoded)) return ['ok'=>false,'error'=>'تعذر قراءة استجابة أجهزة ويندوز.'];
        return ['ok'=>true,'data'=>$decoded];
    }
}

if (!function_exists('ua_output_discover_windows_devices')) {
    function ua_output_discover_windows_devices(): array
    {
        $script = <<<'POWERSHELL'
$defaultName='';$printers=@();$scanners=@();$errors=@()
try{$defaultName=(Get-CimInstance Win32_Printer|Where-Object {$_.Default}|Select-Object -First 1 -ExpandProperty Name)}catch{}
try{Get-Printer|ForEach-Object{$printers += [pscustomobject]@{name=[string]$_.Name;driver=[string]$_.DriverName;port=[string]$_.PortName;status=[string]$_.PrinterStatus;kind=[string]$_.Type;default=([string]$_.Name -eq [string]$defaultName)}}}catch{$errors += 'تعذر اكتشاف طابعات ويندوز.'}
try{Get-PnpDevice -Class Image -ErrorAction Stop|ForEach-Object{$scanners += [pscustomobject]@{name=[string]$_.FriendlyName;status=[string]$_.Status;instance_id=[string]$_.InstanceId}}}catch{$errors += 'تعذر اكتشاف ماسحات ويندوز.'}
ConvertTo-Json -InputObject ([pscustomobject]@{printers=@($printers);scanners=@($scanners);errors=@($errors)}) -Compress -Depth 5
POWERSHELL;
        $result = ua_output_run_powershell_json($script);
        $data = !empty($result['ok']) ? (array)$result['data'] : [];
        return [
            'ok'=>!empty($result['ok']),
            'printers'=>array_values((array)($data['printers'] ?? [])),
            'scanners'=>array_values((array)($data['scanners'] ?? [])),
            'errors'=>!empty($result['ok']) ? array_values((array)($data['errors'] ?? [])) : [(string)($result['error'] ?? 'تعذر اكتشاف الأجهزة.')],
            'detected_at'=>date('Y-m-d H:i:s'),
        ];
    }
}

if (!function_exists('ua_output_load_routes')) {
    function ua_output_load_routes(mysqli $conn, ?string $scopeKey = null): array
    {
        ua_output_ensure_schema($conn);
        $routes = [];
        $scope = $conn->real_escape_string(ua_output_scope_key($scopeKey));
        $result = @$conn->query("SELECT * FROM ua_output_routes WHERE instance_key='{$scope}'");
        if ($result) while ($row = $result->fetch_assoc()) $routes[(string)$row['command_key']] = $row;
        return $routes;
    }
}

if (!function_exists('ua_output_route')) {
    function ua_output_route(mysqli $conn, string $commandKey, ?string $scopeKey = null): array
    {
        $registry = ua_output_command_registry();
        $command = $registry[$commandKey] ?? [];
        if (!$command) return [];
        $scope = ua_output_scope_key($scopeKey);
        $routes = ua_output_load_routes($conn, $scope);
        $saved = $routes[$commandKey] ?? [];
        if (!$saved && preg_match('/^(.*)\:device\:[a-z0-9][a-z0-9_-]{7,63}$/i', $scope, $match)) {
            $fallbackRoutes = ua_output_load_routes($conn, (string)$match[1]);
            $saved = $fallbackRoutes[$commandKey] ?? [];
        }
        return array_merge($command, [
            'command_key'=>$commandKey,
            'device_name'=>(string)($saved['device_name'] ?? ''),
            'agent_host'=>(string)($saved['agent_host'] ?? '127.0.0.1'),
            'agent_token'=>(string)($saved['agent_token'] ?? ''),
            'paper_size'=>(string)($saved['paper_size'] ?? ($command['paper'] ?? '')),
            'copies'=>max(1, (int)($saved['copies'] ?? 1)),
            'is_enabled'=>array_key_exists('is_enabled', $saved) ? (int)$saved['is_enabled'] : 1,
        ]);
    }
}

if (!function_exists('ua_output_save_routes')) {
    function ua_output_save_routes(mysqli $conn, array $post, int $adminId = 0, ?string $scopeKey = null): int
    {
        ua_output_ensure_schema($conn);
        $registry = ua_output_command_registry();
        $routePost = (array)($post['routes'] ?? []);
        $requestedKeys = array_values(array_filter(array_map('strval', (array)($post['route_keys'] ?? []))));
        $routeKeys = $requestedKeys
            ? array_values(array_filter($requestedKeys, static fn(string $key): bool => isset($registry[$key])))
            : array_keys($registry);
        $stmt = $conn->prepare("INSERT INTO ua_output_routes
            (instance_key,command_key,device_type,device_name,agent_host,agent_token,paper_size,copies,is_enabled,updated_by)
            VALUES(?,?,?,?,?,?,?,?,?,?)
            ON DUPLICATE KEY UPDATE device_type=VALUES(device_type),device_name=VALUES(device_name),agent_host=VALUES(agent_host),agent_token=VALUES(agent_token),paper_size=VALUES(paper_size),copies=VALUES(copies),is_enabled=VALUES(is_enabled),updated_by=VALUES(updated_by)");
        if (!$stmt) throw new RuntimeException('تعذر تجهيز حفظ مسارات الأجهزة.');
        $saved = 0;
        $scope = ua_output_scope_key($scopeKey);
        foreach ($routeKeys as $key) {
            $command = $registry[$key];
            $input = (array)($routePost[$key] ?? []);
            $type = (string)$command['type'];
            $device = trim((string)($input['device_name'] ?? ''));
            $host = trim((string)($input['agent_host'] ?? '127.0.0.1'));
            if ($host === '') $host = '127.0.0.1';
            $token = trim((string)($input['agent_token'] ?? ''));
            $paper = trim((string)($input['paper_size'] ?? ($command['paper'] ?? '')));
            $copies = max(1, min(99, (int)($input['copies'] ?? 1)));
            $enabled = isset($input['is_enabled']) ? 1 : 0;
            if (mb_strlen($device) > 255) $device = mb_substr($device, 0, 255);
            if (mb_strlen($host) > 180) $host = mb_substr($host, 0, 180);
            if (mb_strlen($token) > 160) $token = mb_substr($token, 0, 160);
            $stmt->bind_param('sssssssiii', $scope, $key, $type, $device, $host, $token, $paper, $copies, $enabled, $adminId);
            if (!$stmt->execute()) throw new RuntimeException('تعذر حفظ المسار: ' . (string)$command['label']);
            $saved++;
        }
        $stmt->close();
        return $saved;
    }
}

if (!function_exists('ua_output_log_job')) {
    function ua_output_log_job(
        mysqli $conn,
        string $commandKey,
        string $status,
        string $message = '',
        string $referenceId = '',
        ?string $scopeKey = null
    ): void
    {
        $route = ua_output_route($conn, $commandKey, $scopeKey);
        if (!$route) return;
        $actor = ua_output_session_actor($conn);
        $adminId = $actor['type'] === 'admin' ? (int)$actor['id'] : 0;
        $actorType = (string)($actor['type'] ?? 'system');
        $actorId = (int)($actor['id'] ?? 0);
        $type = (string)$route['type'];
        $device = (string)$route['device_name'];
        $host = (string)$route['agent_host'];
        $source = (string)$route['source'];
        $scope = ua_output_scope_key($scopeKey);
        $stmt = @$conn->prepare('INSERT INTO ua_output_jobs(instance_key,command_key,device_type,device_name,agent_host,source_path,reference_id,job_status,status_message,admin_id,actor_type,actor_id) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)');
        if ($stmt) {
            $stmt->bind_param('sssssssssisi', $scope, $commandKey, $type, $device, $host, $source, $referenceId, $status, $message, $adminId, $actorType, $actorId);
            @$stmt->execute();
            $stmt->close();
        }
    }
}

if (!function_exists('ua_output_print_bootstrap')) {
    function ua_output_print_bootstrap(mysqli $conn, string $commandKey, string $referenceId = '', ?string $scopeKey = null): void
    {
        if ($scopeKey === null) {
            $requestedAgentId = (int)($_GET['agent'] ?? $_POST['agent'] ?? 0);
            if ($requestedAgentId > 0 && !empty($_SESSION['agent_portal_' . $requestedAgentId])) {
                $scopeKey = ua_output_agent_scope($requestedAgentId);
            }
        }
        $scopeKey = ua_output_scope_key($scopeKey);
        $route = ua_output_route($conn, $commandKey, $scopeKey);
        if (!$route || $route['type'] !== 'printer') return;
        $payload = [
            'commandKey'=>$commandKey,
            'deviceName'=>(string)$route['device_name'],
            'agentHost'=>(string)$route['agent_host'],
            'agentToken'=>(string)$route['agent_token'],
            'paperSize'=>(string)$route['paper_size'],
            'copies'=>(int)$route['copies'],
            'enabled'=>(bool)$route['is_enabled'],
            'referenceId'=>$referenceId,
            'scopeKey'=>$scopeKey,
            'scopeBase'=>(string)(preg_replace('/\:device\:[a-z0-9_-]{8,64}$/i', '', $scopeKey) ?? $scopeKey),
            'deviceScoped'=>true,
            'logUrl'=>'output_job_log.php',
            'proxyUrl'=>'output_agent_proxy.php',
            'configUrl'=>'output_route_config.php',
            'csrfToken'=>function_exists('ua_csrf_token') ? ua_csrf_token() : '',
        ];
        echo '<script>window.UA_OUTPUT_ROUTE=' . json_encode($payload, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES) . ';</script>';
        echo '<script src="assets/js/ua_output_router.js?v=' . (int)(@filemtime(__DIR__ . '/assets/js/ua_output_router.js') ?: 1) . '"></script>';
    }
}
