<?php
require_once __DIR__ . '/../config.php';

class XrayAPI {
    protected $url;
    protected $base_url; // URL بدون path (مثلاً https://example.com:2053)
    protected $username;
    protected $password;
    protected $token;
    protected $base_path = ''; // مسیر اضافی از URL (مثلاً /mute)
    protected $base_endpoint = '/panel'; // endpoint پیش‌فرض
    protected $successful_login_url = null; // URL موفق login برای استفاده در endpoint های دیگر
    protected $proxy_type;
    protected $proxy_host;
    protected $proxy_port;
    protected $proxy_username;
    protected $proxy_password;
    protected $use_proxy_on_error;
    protected $subscription_base_url = null; // آدرس پایه برای subscription link
    protected $config_domain = null; // دامین تانل شده برای لینک کانفیگ
    
    public function __construct($url, $username, $password, $proxy_config = null, $config_domain = null) {
        $this->url = rtrim($url, '/');
        
        // اطمینان از وجود scheme (http/https)
        if (!preg_match('#^https?://#i', $this->url)) {
            $this->url = 'http://' . $this->url;
        }
        
        $this->username = $username;
        $this->password = $password;
        
        // استخراج base path از URL (مثلاً /mute از https://example.com:2053/mute)
        $url_parts = parse_url($this->url);
        $this->base_path = isset($url_parts['path']) ? rtrim($url_parts['path'], '/') : '';
        
        // اگر URL شامل path است، آن را از base URL جدا کن
        if ($this->base_path) {
            // base URL بدون path
            $this->base_url = $url_parts['scheme'] . '://' . $url_parts['host'];
            if (isset($url_parts['port'])) {
                $this->base_url .= ':' . $url_parts['port'];
            }
        } else {
            $this->base_url = $this->url;
        }
        
        // تنظیمات proxy
        if ($proxy_config) {
            $this->proxy_type = $proxy_config['type'] ?? null;
            $this->proxy_host = $proxy_config['host'] ?? null;
            $this->proxy_port = $proxy_config['port'] ?? null;
            $this->proxy_username = $proxy_config['username'] ?? null;
            $this->proxy_password = $proxy_config['password'] ?? null;
            $this->use_proxy_on_error = $proxy_config['use_on_error'] ?? false;
        }
        
        if ($config_domain) {
            $this->config_domain = $config_domain;
        }
        
        $this->login();
    }
    
    /**
     * ورود به پنل و دریافت توکن
     */
    private function login() {
        try {
            return $this->tryLogin(false);
        } catch (Exception $e) {
            // اگر خطا داشت و proxy فعال است، دوباره با proxy امتحان کن
            if ($this->use_proxy_on_error && $this->proxy_type && $this->proxy_host) {
                try {
                    return $this->tryLogin(true);
                } catch (Exception $e2) {
                    throw new Exception("خطا در اتصال به پنل (با و بدون proxy): " . $e2->getMessage());
                }
            }
            throw $e;
        }
    }
    
    /**
     * تلاش برای ورود (با یا بدون proxy)
     */
    private function tryLogin($use_proxy = false) {
        // امتحان endpoint های مختلف برای login
        $login_endpoints = [
            '/login',
            '/xui/login',
            '/api/login',
            '/panel/login'
        ];
        
        // استفاده از base_url برای cookie file تا cookie ها مشترک باشند
        // استفاده از base_url + base_path برای cookie file تا cookie ها مشترک باشند
        $cookie_file = sys_get_temp_dir() . '/3xui_cookie_' . md5($this->base_url . ($this->base_path ?? '')) . '.txt';
        $last_error = '';
        $last_http = 0;
        $last_body = '';
        
        foreach ($login_endpoints as $login_endpoint) {
            // Login معمولاً بدون base_path کار می‌کند
            // اما اگر base_path وجود دارد، ابتدا بدون آن امتحان می‌کنیم
            $login_urls = [];
            if ($this->base_path) {
                // اول بدون base_path امتحان کن (مثلاً https://example.com:2053/login)
                $login_urls[] = $this->base_url . $login_endpoint;
                // سپس با base_path (مثلاً https://example.com:2053/mute/login)
                $login_urls[] = $this->url . $login_endpoint;
            } else {
                $login_urls[] = $this->url . $login_endpoint;
            }
            
            foreach ($login_urls as $login_url) {
                $ch = curl_init();
                $options = [
                    CURLOPT_URL => $login_url,
                    CURLOPT_FOLLOWLOCATION => true,
                    CURLOPT_MAXREDIRS => 5,
                    CURLOPT_POST => true,
                    CURLOPT_POSTFIELDS => json_encode([
                        'username' => $this->username,
                        'password' => $this->password
                    ]),
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_HTTPHEADER => [
                        'Content-Type: application/json'
                    ],
                    CURLOPT_COOKIEJAR => $cookie_file,
                    CURLOPT_COOKIEFILE => $cookie_file,
                    CURLOPT_TIMEOUT => 30,
                    CURLOPT_CONNECTTIMEOUT => 10,
                    CURLOPT_SSL_VERIFYPEER => false, // پنل‌ها اغلب گواهی self-signed دارند
                    CURLOPT_SSL_VERIFYHOST => 0
                ];
            
            // تنظیم proxy در صورت نیاز
            if ($use_proxy && $this->proxy_type && $this->proxy_host) {
                if ($this->proxy_type === 'socks5') {
                    $proxy_url = 'socks5://';
                    if ($this->proxy_username && $this->proxy_password) {
                        $proxy_url .= urlencode($this->proxy_username) . ':' . urlencode($this->proxy_password) . '@';
                    }
                    $proxy_url .= $this->proxy_host . ':' . ($this->proxy_port ?? 1080);
                    
                    $options[CURLOPT_PROXY] = $proxy_url;
                    $options[CURLOPT_PROXYTYPE] = CURLPROXY_SOCKS5;
                }
            }
            
                curl_setopt_array($ch, $options);
                
                $response = curl_exec($ch);
                $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                $error = curl_error($ch);
                curl_close($ch);
                
                if ($error) {
                    $last_error = "CURL: $error (URL: $login_url)";
                    $last_http = $httpCode;
                    $last_body = $response;
                    continue;
                }
                if ($httpCode === 200) {
                    $data = is_string($response) ? json_decode($response, true) : $response;
                    // پنل 3x-ui: success: true و گاهی فقط کوکی برمی‌گرداند (بدون token در JSON)
                    $ok = isset($data['success']) && $data['success'];
                    if (!$ok && isset($data['msg']) && (strpos($data['msg'], 'uccess') !== false || $data['msg'] === 'Login Successfully')) {
                        $ok = true;
                    }
                    if ($ok) {
                        $this->token = $data['token'] ?? null; // ممکن است null باشد؛ احراز با کوکی انجام می‌شود
                        $this->base_endpoint = rtrim($login_endpoint, '/login');
                        if (empty($this->base_endpoint) || $this->base_endpoint === '/') {
                            $this->base_endpoint = '';
                        }
                        $this->successful_login_url = $login_url;
                        error_log("Login successful at: $login_url, base_endpoint: '" . ($this->base_endpoint ?: '(empty)') . "'");
                        return true;
                    }
                    if (isset($data['success']) && $data['success'] === false && !empty($data['msg'])) {
                        $last_error = $data['msg'];
                        $last_http = $httpCode;
                        $last_body = $response;
                    }
                } else {
                    $last_error = "HTTP $httpCode";
                    $last_http = $httpCode;
                    $last_body = $response;
                }
            }
        }
        
        $msg = "ورود به پنل ناموفق بود.";
        if (!empty($last_error)) {
            $msg .= " آخرین خطا: " . (is_string($last_error) ? $last_error : "HTTP $last_http");
        }
        if (!empty($last_body) && is_string($last_body) && strlen($last_body) < 500) {
            $msg .= " پاسخ سرور: " . $last_body;
        }
        throw new Exception($msg);
    }
    
    /**
     * ساخت URL کامل با base_path
     */
    private function buildUrl($endpoint) {
        // endpoint باید با / شروع شود
        if (substr($endpoint, 0, 1) !== '/') {
            $endpoint = '/' . $endpoint;
        }
        
        // اگر base_path وجود دارد، از base_url استفاده کن و base_path را اضافه کن
        if ($this->base_path) {
            $full_url = $this->base_url . $this->base_path . $endpoint;
        } else {
            $full_url = $this->url . $endpoint;
        }
        
        return $full_url;
    }
    
    /**
     * ارسال درخواست به API
     */
    private function request($endpoint, $method = 'GET', $data = null, $use_proxy = false) {
        // اضافه کردن base_path به endpoint
        $full_endpoint = $this->base_path . $endpoint;
        
        try {
            return $this->tryRequest($full_endpoint, $method, $data, $use_proxy);
        } catch (Exception $e) {
            // اگر خطا داشت، بدون base_path امتحان کن
            try {
                return $this->tryRequest($endpoint, $method, $data, $use_proxy);
            } catch (Exception $e2) {
                // اگر خطا داشت و proxy فعال است، دوباره با proxy امتحان کن
                if (!$use_proxy && $this->use_proxy_on_error && $this->proxy_type && $this->proxy_host) {
                    try {
                        return $this->tryRequest($full_endpoint, $method, $data, true);
                    } catch (Exception $e3) {
                        throw new Exception("خطا در ارسال درخواست (با و بدون proxy): " . $e3->getMessage());
                    }
                }
                throw $e2;
            }
        }
    }
    
    /**
     * تلاش برای ارسال درخواست (با یا بدون proxy)
     */
    private function tryRequest($endpoint, $method = 'GET', $data = null, $use_proxy = false) {
        $ch = curl_init();
        // ساخت URL کامل
        // اگر endpoint خودش با base_path شروع می‌شود، مستقیماً از base_url استفاده کن
        if ($this->base_path && strpos($endpoint, $this->base_path) === 0) {
            $url = $this->base_url . $endpoint;
        } else {
            $url = $this->buildUrl($endpoint);
        }
        
        $headers = [
            'Content-Type: application/json'
        ];
        
        if ($this->token) {
            $headers[] = 'Authorization: Bearer ' . $this->token;
        }
        
        // استفاده از base_url برای cookie file تا cookie ها مشترک باشند
        // استفاده از base_url برای cookie file تا cookie ها مشترک باشند
        $cookie_file = sys_get_temp_dir() . '/3xui_cookie_' . md5($this->base_url . ($this->base_path ?? '')) . '.txt';
        
        $options = [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS => 5,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_COOKIEFILE => $cookie_file,
            CURLOPT_COOKIEJAR => $cookie_file,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => 0
        ];
        
        // تنظیم proxy در صورت نیاز
        if ($use_proxy && $this->proxy_type && $this->proxy_host) {
            if ($this->proxy_type === 'socks5') {
                $proxy_url = 'socks5://';
                if ($this->proxy_username && $this->proxy_password) {
                    $proxy_url .= urlencode($this->proxy_username) . ':' . urlencode($this->proxy_password) . '@';
                }
                $proxy_url .= $this->proxy_host . ':' . ($this->proxy_port ?? 1080);
                
                $options[CURLOPT_PROXY] = $proxy_url;
                $options[CURLOPT_PROXYTYPE] = CURLPROXY_SOCKS5;
            }
        }
        
        if ($method === 'POST') {
            $options[CURLOPT_POST] = true;
            if ($data) {
                $options[CURLOPT_POSTFIELDS] = json_encode($data);
            }
        }
        
        curl_setopt_array($ch, $options);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        if ($error) {
            throw new Exception("خطا در ارسال درخواست: $error");
        }
        
        if ($httpCode !== 200) {
            throw new Exception("خطا در دریافت پاسخ: HTTP $httpCode");
        }
        
        return json_decode($response, true);
    }
    
    /**
     * دریافت لیست inbound ها
     */
    public function getInbounds() {
        // بر اساس کد py3xui (که whale-panel استفاده می‌کند):
        // endpoint صحیح: panel/api/inbounds/list
        // نه /xui/API/inbounds/ که در مستندات ذکر شده بود
        $endpoints = [];
        
        // endpoint اصلی که py3xui استفاده می‌کند
        $primary_endpoint = 'panel/api/inbounds/list';
        
        // اگر base_path وجود دارد (مثلاً /mute)، باید endpoint ها را با و بدون base_path امتحان کنیم
        if ($this->base_path) {
            // endpoint ها با base_path
            $path_with_base = $this->base_path . '/' . $primary_endpoint;
            // جلوگیری از تکرار panel/panel
            $path_with_base = str_replace('/panel/panel', '/panel', $path_with_base);
            $endpoints[] = $path_with_base;
            
            $endpoints[] = $this->base_path . '/panel/api/inbounds/list';
            $endpoints[] = $this->base_path . '/panel/inbound/list';
            $endpoints[] = $this->base_path . '/xui/API/inbounds/';
            $endpoints[] = $this->base_path . '/xui/API/inbounds';
            $endpoints[] = $this->base_path . '/API/inbounds/';
            $endpoints[] = $this->base_path . '/API/inbounds';
            
            // تمیزکاری مسیرها برای جلوگیری از تکرار panel/panel
            foreach ($endpoints as $k => $ep) {
                $endpoints[$k] = str_replace('/panel/panel', '/panel', $ep);
            }
        }
        
        // اگر successful_login_url موجود است، از آن برای ساخت endpoint استفاده کن
        if ($this->successful_login_url) {
            $login_url_parts = parse_url($this->successful_login_url);
            $login_path = isset($login_url_parts['path']) ? dirname($login_url_parts['path']) : '';
            
            // ساخت endpoint ها بر اساس login URL
            if ($login_path && $login_path !== '/' && $login_path !== '.') {
                // استفاده از endpoint صحیح py3xui
                $endpoints[] = $login_path . '/' . $primary_endpoint;
                $endpoints[] = $login_path . '/panel/api/inbounds/list';
                $endpoints[] = $login_path . '/panel/inbound/list';
                $endpoints[] = $login_path . '/xui/API/inbounds/';
                $endpoints[] = $login_path . '/xui/API/inbounds';
            }
        }
        
        // اگر base_endpoint از login ذخیره شده، از آن استفاده کن
        if ($this->base_endpoint && $this->base_endpoint !== '/panel' && $this->base_endpoint !== '/') {
            // استفاده از endpoint صحیح py3xui
            $endpoints[] = $this->base_endpoint . '/' . $primary_endpoint;
            $endpoints[] = $this->base_endpoint . '/panel/api/inbounds/list';
            $endpoints[] = $this->base_endpoint . '/panel/inbound/list';
        }
        
        // endpoint های استاندارد (اولویت با endpoint صحیح py3xui)
        $standard_endpoints = [
            $primary_endpoint, // اول این را امتحان کن (طبق py3xui)
            'panel/api/inbounds/list', // بدون slash اول
            '/panel/api/inbounds/list', // با slash اول
            '/xui/API/inbounds/', // fallback (مستندات قدیمی)
            '/xui/API/inbounds', // بدون slash آخر
            '/panel/inbound/list', // fallback برای نسخه‌های قدیمی
            '/xui/inbound/list', // fallback
            '/api/inbound/list', // fallback
            '/inbound/list' // fallback
        ];
        
        // اگر base_path وجود دارد، endpoint های استاندارد را هم با base_path اضافه کن
        if ($this->base_path) {
            foreach ($standard_endpoints as $ep) {
                // حذف slash اول اگر وجود دارد
                $ep_clean = ltrim($ep, '/');
                $endpoints[] = $this->base_path . '/' . $ep_clean;
            }
        }
        
        // endpoint های استاندارد بدون base_path (برای زمانی که API endpoint ها مستقیماً از root هستند)
        $endpoints = array_merge($endpoints, $standard_endpoints);
        
        // حذف تکراری ها
        $endpoints = array_unique($endpoints);
        
        $cookie_file = sys_get_temp_dir() . '/3xui_cookie_' . md5($this->base_url . ($this->base_path ?? '')) . '.txt';
        
        // بررسی وجود cookie file
        if (!file_exists($cookie_file)) {
            error_log("Cookie file not found: $cookie_file. Attempting to re-login...");
            // تلاش برای login مجدد
            try {
                $this->tryLogin(false);
            } catch (Exception $e) {
                throw new Exception("Cookie file موجود نیست و login مجدد ناموفق بود: " . $e->getMessage());
            }
        }
        
        $last_error = null;
        foreach ($endpoints as $endpoint) {
            try {
                // ساخت URL کامل
                // اگر endpoint خودش با base_path شروع می‌شود، مستقیماً از base_url استفاده کن
                if ($this->base_path && strpos($endpoint, $this->base_path) === 0) {
                    // endpoint خودش base_path دارد (مثلاً /mute/xui/API/inbounds/)
                    $url = $this->base_url . $endpoint;
                } else if ($this->base_path && strpos($endpoint, '/') === 0) {
                    // endpoint بدون base_path است (مثلاً /xui/API/inbounds/)
                    // باید هم با base_path و هم بدون آن امتحان کنیم
                    // اول با base_path امتحان می‌کنیم
                    $url = $this->base_url . $this->base_path . $endpoint;
                } else {
                    // استفاده از buildUrl که خودش base_path را اضافه می‌کند
                    $url = $this->buildUrl($endpoint);
                }
                
                error_log("Attempting getInbounds with endpoint: $endpoint, URL: $url, base_path: " . ($this->base_path ?: '(empty)'));
                
                $ch = curl_init();
                $headers = [
                    'Content-Type: application/json',
                    'Accept: application/json',
                    'X-Requested-With: XMLHttpRequest' // برای برخی نسخه‌های x-ui
                ];
                
                if ($this->token) {
                    $headers[] = 'Authorization: Bearer ' . $this->token;
                }
                
                $options = [
                    CURLOPT_URL => $url,
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_FOLLOWLOCATION => true,
                    CURLOPT_MAXREDIRS => 5,
                    CURLOPT_HTTPHEADER => $headers,
                    CURLOPT_COOKIEFILE => $cookie_file,
                    CURLOPT_COOKIEJAR => $cookie_file,
                    CURLOPT_TIMEOUT => 30,
                    CURLOPT_CONNECTTIMEOUT => 10,
                    CURLOPT_SSL_VERIFYPEER => false, // برای تست (در production باید true باشد)
                    CURLOPT_SSL_VERIFYHOST => false,  // برای تست
                    CURLOPT_VERBOSE => false, // برای دیباگ می‌توان true کرد
                    CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' // برخی پنل‌ها نیاز دارند
                ];
                
                curl_setopt_array($ch, $options);
                
                $response = curl_exec($ch);
                $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                $error = curl_error($ch);
                $effective_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
                curl_close($ch);
                
                if ($error) {
                    throw new Exception("خطا در ارسال درخواست: $error");
                }
                
                error_log("getInbounds response: HTTP $httpCode for URL: $url (Effective: $effective_url)");
                
                if ($httpCode === 200) {
                    $result = json_decode($response, true);
                    // بررسی ساختارهای مختلف پاسخ
                    if ($result !== null) {
                        // اگر یک آرایه مستقیم است (بدون success/obj/data)
                        if (is_array($result) && !isset($result['success']) && !isset($result['obj']) && !isset($result['data']) && !isset($result['msg'])) {
                            // احتمالاً لیست مستقیم inbound ها است
                            error_log("getInbounds success: Direct array response");
                            return ['obj' => $result, 'success' => true];
                        }
                        // اگر ساختار استاندارد دارد
                        if (isset($result['success']) || isset($result['obj']) || isset($result['data'])) {
                            error_log("getInbounds success: Standard response structure");
                            return $result;
                        }
                        // اگر آرایه خالی نیست
                        if (is_array($result) && !empty($result)) {
                            error_log("getInbounds success: Non-empty array");
                            return ['obj' => $result, 'success' => true];
                        }
                    }
                    // اگر response خالی بود یا format اشتباه بود
                    $last_error = "پاسخ نامعتبر از سرور. URL: $url (Effective: $effective_url), Response: " . substr($response, 0, 200);
                    error_log("getInbounds invalid response: " . $last_error);
                    continue;
                } else if ($httpCode === 301 || $httpCode === 302) {
                    // اگر redirect بود، endpoint بعدی را امتحان کن
                    error_log("Redirect $httpCode از $url به $effective_url");
                    $last_error = "Redirect $httpCode";
                    continue;
                } else {
                    $last_error = "HTTP $httpCode برای URL: $url (Effective: $effective_url). Response: " . substr($response, 0, 200);
                    error_log("getInbounds HTTP error: " . $last_error);
                    continue;
                }
            } catch (Exception $e) {
                // لاگ خطا برای دیباگ
                $last_error = $e->getMessage();
                error_log("خطا در getInbounds با endpoint $endpoint (URL: " . ($url ?? 'N/A') . "): " . $last_error);
                // ادامه به endpoint بعدی
                continue;
            }
        }
        
        // اگر هیچکدام کار نکرد، خطا بده با جزئیات بیشتر
        $cookie_exists = file_exists($cookie_file) ? 'بله' : 'خیر';
        $cookie_size = file_exists($cookie_file) ? filesize($cookie_file) . ' bytes' : '0';
        $cookie_content = file_exists($cookie_file) ? file_get_contents($cookie_file) : '';
        
        throw new Exception("خطا در دریافت inbound ها: هیچ endpoint معتبری یافت نشد. " .
            "base_path: '" . $this->base_path . "', " .
            "base_url: " . $this->base_url . ", " .
            "Cookie file exists: $cookie_exists ($cookie_size). " .
            "Last error: " . ($last_error ?? 'N/A') . ". " .
            "Cookie content preview: " . substr($cookie_content, 0, 100));
    }
    
    /**
     * دریافت لیست کاربران آنلاین (کلاینت‌های متصل به سرور x-ui).
     * بر اساس مستندات: POST /xui/API/inbounds/onlines
     * @param int|null $inboundId اگر مشخص شود فقط آنلاین‌های این اینباند؛ وگرنه همه (در صورت پشتیبانی پنل)
     * @return array لیست email یا id های آنلاین؛ در صورت خطا آرایه خالی برمی‌گرداند (exception نمی‌زند)
     */
    public function getOnlines($inboundId = null) {
        $body = $inboundId !== null ? ['id' => (int)$inboundId] : [];
        $attempts = [
            ['method' => 'POST', 'endpoint' => '/xui/API/inbounds/onlines'],
            ['method' => 'POST', 'endpoint' => '/xui/API/inbounds/onlines/'],
            ['method' => 'GET', 'endpoint' => '/xui/API/inbounds/onlines'],
            ['method' => 'POST', 'endpoint' => '/panel/api/inbounds/onlines'],
            ['method' => 'GET', 'endpoint' => '/panel/api/inbounds/onlines'],
        ];
        foreach ($attempts as $a) {
            try {
                $result = $a['method'] === 'POST'
                    ? $this->request($a['endpoint'], 'POST', $body)
                    : $this->request($a['endpoint'], 'GET');
                if ($result === null) continue;
                $list = [];
                if (isset($result['obj']) && is_array($result['obj'])) {
                    $list = $result['obj'];
                } elseif (isset($result['data']) && is_array($result['data'])) {
                    $list = $result['data'];
                } elseif (is_array($result) && !isset($result['success'])) {
                    $list = $result;
                }
                $emails = [];
                foreach ($list as $item) {
                    if (is_string($item)) {
                        $emails[] = $item;
                    } elseif (is_array($item) && isset($item['email'])) {
                        $emails[] = $item['email'];
                    } elseif (is_array($item) && isset($item['id'])) {
                        $emails[] = (string)$item['id'];
                    }
                }
                return array_values(array_unique($emails));
            } catch (Exception $e) {
                continue;
            }
        }
        return [];
    }
    
    /**
     * افزودن کاربر جدید
     */
    public function addClient($inboundId, $email, $remark, $trafficLimit = 0, $expiryTime = 0) {
        // بر اساس مستندات: POST /xui/API/inbounds/addClient/
        // اما ابتدا باید inbound را دریافت کنیم
        $inbounds = $this->getInbounds();
        $inbound_list = [];
        if (isset($inbounds['obj']) && is_array($inbounds['obj'])) {
            $inbound_list = $inbounds['obj'];
        } elseif (isset($inbounds['data']) && is_array($inbounds['data'])) {
            $inbound_list = $inbounds['data'];
        } elseif (is_array($inbounds)) {
            $inbound_list = $inbounds;
        }
        
        $inbound = null;
        foreach ($inbound_list as $inb) {
            if ($inb['id'] == $inboundId) {
                $inbound = $inb;
                break;
            }
        }
        
        if (!$inbound) {
            throw new Exception("Inbound یافت نشد");
        }
        
        // استفاده از API addClient (طبق py3xui)
        // endpoint صحیح: panel/api/inbounds/addClient
        // ساختار داده: {"id": inbound_id, "settings": json.dumps({"clients": [...]})}
        // subId برای لینک subscription در 3x-ui ضروری است (نه inboundId)
        $uuid = $this->generateUUID();
        $subId = $this->generateUUID();
        
        // در نسخه 2.6.6 پنل 3x-ui، totalGB باید عدد صحیح باشد (int64)
        // trafficLimit به بایت است و باید بدون تغییر ارسال شود
        $totalGB = 0;
        if ($trafficLimit > 0) {
            $totalGB = (int)$trafficLimit;
        }
        
        error_log("addClient: trafficLimit=$trafficLimit bytes, totalGB=$totalGB (Bytes), email=$email");
        
        // در نسخه 2.6.6، totalGB باید عدد صحیح باشد و اگر 0 است (نامحدود)، باید 0 بماند
        // اما اگر مقدار مثبت است، باید به صورت عدد صحیح ارسال شود
        $client = [
            'id' => $uuid,
            'email' => $email,
            'limitIp' => 0,
            'totalGB' => $totalGB, // عدد صحیح: 0 = نامحدود، >0 = محدود به GB
            'expiryTime' => $expiryTime > 0 ? $expiryTime * 1000 : 0,
            'enable' => true,
            'tgId' => '',
            'subId' => $subId
        ];
        
        error_log("addClient client object: " . json_encode($client, JSON_UNESCAPED_UNICODE));
        
        // ساختار داده طبق py3xui
        $clientData = [
            'id' => $inboundId,
            'settings' => json_encode([
                'clients' => [$client]
            ])
        ];
        
        // امتحان endpoint های مختلف (اولویت با endpoint صحیح py3xui)
        $endpoints = [];
        
        // اگر base_path وجود دارد، endpoint ها را با base_path اضافه کن
        if ($this->base_path) {
            $endpoints[] = str_replace('/panel/panel', '/panel', $this->base_path . '/panel/api/inbounds/addClient');
            $endpoints[] = $this->base_path . '/xui/API/inbounds/addClient/';
            $endpoints[] = $this->base_path . '/xui/inbound/addClient';
            $endpoints[] = $this->base_path . '/panel/inbound/addClient';
            
            // تمیزکاری مسیرها
            foreach ($endpoints as $k => $ep) {
                $endpoints[$k] = str_replace('/panel/panel', '/panel', $ep);
            }
        }
        
        // endpoint های استاندارد
        $endpoints = array_merge($endpoints, [
            'panel/api/inbounds/addClient', // اول این را امتحان کن (طبق py3xui)
            '/panel/api/inbounds/addClient', // با slash اول
            '/xui/API/inbounds/addClient/', // fallback
            '/xui/inbound/addClient', // fallback
            '/panel/inbound/addClient' // fallback
        ]);
        
        foreach ($endpoints as $endpoint) {
            try {
                error_log("Attempting addClient with endpoint: $endpoint");
                error_log("Client data: " . json_encode($clientData, JSON_UNESCAPED_UNICODE));
                
                $result = $this->tryRequest($endpoint, 'POST', $clientData, false);
                if ($result && (isset($result['success']) || isset($result['obj']))) {
                    error_log("addClient success with endpoint: $endpoint");
                    // اضافه کردن UUID و subId به نتیجه (subId برای subscription ضروری است)
                    $result['client_uuid'] = $uuid;
                    $result['client_email'] = $email;
                    $result['client_sub_id'] = $subId;
                    return $result;
                }
            } catch (Exception $e) {
                error_log("addClient failed with endpoint $endpoint: " . $e->getMessage());
                continue;
            }
        }
        
        // Fallback: استفاده از روش قدیمی (update inbound)
        $clients = $inbound['clients'] ?? [];
        $uuid = $this->generateUUID();
        
        $subId = $this->generateUUID();
        // در نسخه 2.6.6 پنل 3x-ui، totalGB باید عدد صحیح باشد (int64)
        $totalGB = 0;
        if ($trafficLimit > 0) {
            $totalGB = (int)$trafficLimit;
        }
        
        $newClient = [
            'id' => $uuid,
            'email' => $email,
            'limitIp' => 0,
            'totalGB' => $totalGB,
            'expiryTime' => $expiryTime > 0 ? $expiryTime * 1000 : 0,
            'enable' => true,
            'tgId' => '',
            'subId' => $subId
        ];
        
        $clients[] = $newClient;
        
        $updateData = [
            'id' => $inboundId,
            'remark' => $inbound['remark'],
            'port' => $inbound['port'],
            'protocol' => $inbound['protocol'],
            'settings' => json_encode([
                'clients' => $clients,
                'decryption' => $inbound['protocol'] === 'vless' ? 'none' : null,
                'fallbacks' => []
            ]),
            'streamSettings' => $inbound['streamSettings'] ?? '{}',
            'sniffing' => $inbound['sniffing'] ?? '{}',
            'tag' => $inbound['tag'] ?? ''
        ];
        
        $result = $this->request('/xui/API/inbounds/update/' . $inboundId, 'POST', $updateData);
        // اضافه کردن UUID و subId به نتیجه (برای fallback method)
        if ($result) {
            $result['client_uuid'] = $uuid;
            $result['client_email'] = $email;
            $result['client_sub_id'] = $subId;
        }
        return $result;
    }
    
    /**
     * حذف کاربر
     */
    public function deleteClient($inboundId, $clientId) {
        // ساخت لیستی از endpoint های محتمل برای حذف کلاینت
        $endpoints = [];
        
        // اگر base_path وجود دارد، endpoint ها را با base_path اضافه کن
        if ($this->base_path) {
            $bp = rtrim($this->base_path, '/');
            $endpoints[] = $bp . '/panel/api/inbounds/' . $inboundId . '/delClient/' . $clientId;
            $endpoints[] = $bp . '/xui/API/inbounds/' . $inboundId . '/delClient/' . $clientId;
        }

        $endpoints = array_merge($endpoints, [
            '/panel/api/inbounds/' . $inboundId . '/delClient/' . $clientId,
            'panel/api/inbounds/' . $inboundId . '/delClient/' . $clientId,
            '/xui/API/inbounds/' . $inboundId . '/delClient/' . $clientId,
            '/xui/inbound/' . $inboundId . '/delClient/' . $clientId,
            '/panel/inbound/' . $inboundId . '/delClient/' . $clientId
        ]);
        
        foreach ($endpoints as $endpoint) {
            try {
                error_log("Attempting deleteClient with endpoint: $endpoint");
                $result = $this->tryRequest($endpoint, 'POST', null, false);
                if ($result && (isset($result['success']) || isset($result['obj']))) {
                    error_log("deleteClient success with endpoint: $endpoint");
                    return $result;
                }
            } catch (Exception $e) {
                error_log("deleteClient failed with endpoint $endpoint: " . $e->getMessage());
                continue;
            }
        }
        
        // Fallback: استفاده از روش قدیمی (update inbound)
        error_log("deleteClient: falling back to update inbound method for ID: $inboundId");
        $inbounds = $this->getInbounds();
        $inbound_list = isset($inbounds['obj']) ? $inbounds['obj'] : (isset($inbounds['data']) ? $inbounds['data'] : []);
        
        $inbound = null;
        foreach ($inbound_list as $inb) {
            if ($inb['id'] == $inboundId) {
                $inbound = $inb;
                break;
            }
        }
        
        if (!$inbound) {
            throw new Exception("Inbound یافت نشد");
        }
        
        $clients = $inbound['clients'] ?? [];
        $clients = array_filter($clients, function($client) use ($clientId) {
            return $client['id'] !== $clientId;
        });
        
        $clients = array_values($clients);
        
        $updateData = [
            'id' => $inboundId,
            'remark' => $inbound['remark'],
            'port' => $inbound['port'],
            'protocol' => $inbound['protocol'],
            'settings' => json_encode([
                'clients' => $clients,
                'decryption' => $inbound['protocol'] === 'vless' ? 'none' : null,
                'fallbacks' => []
            ]),
            'streamSettings' => $inbound['streamSettings'] ?? '{}',
            'sniffing' => $inbound['sniffing'] ?? '{}',
            'tag' => $inbound['tag'] ?? ''
        ];
        
        return $this->request('/xui/API/inbounds/update/' . $inboundId, 'POST', $updateData);
    }
    
    /**
     * به‌روزرسانی کاربر
     */
    public function updateClient($inboundId, $clientId, $email, $remark, $trafficLimit = 0, $expiryTime = 0) {
        // بر اساس مستندات: POST /xui/API/inbounds/updateClient/:clientId
        // در نسخه 2.6.6 پنل 3x-ui، totalGB باید عدد صحیح باشد (int64)
        $totalGB = 0;
        if ($trafficLimit > 0) {
            $totalGB = (int)$trafficLimit;
        }
        
        $updateData = [
            'id' => $clientId,
            'email' => $email,
            'totalGB' => $totalGB,
            'expiryTime' => $expiryTime > 0 ? $expiryTime * 1000 : 0
        ];
        
        $endpoints = [
            '/xui/API/inbounds/updateClient/' . $clientId,
            '/xui/inbound/updateClient/' . $clientId,
            '/panel/inbound/updateClient/' . $clientId
        ];
        
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'POST', $updateData, false);
                if ($result && (isset($result['success']) || isset($result['obj']))) {
                    return $result;
                }
            } catch (Exception $e) {
                continue;
            }
        }
        
        // Fallback: استفاده از روش قدیمی (update inbound)
        $inbounds = $this->getInbounds();
        $inbound_list = isset($inbounds['obj']) ? $inbounds['obj'] : (isset($inbounds['data']) ? $inbounds['data'] : []);
        
        $inbound = null;
        foreach ($inbound_list as $inb) {
            if ($inb['id'] == $inboundId) {
                $inbound = $inb;
                break;
            }
        }
        
        if (!$inbound) {
            throw new Exception("Inbound یافت نشد");
        }
        
        $clients = $inbound['clients'] ?? [];
        // در نسخه 2.6.6 پنل 3x-ui، totalGB باید عدد صحیح باشد (int64)
        $totalGB = 0;
        if ($trafficLimit > 0) {
            $totalGB = (int)$trafficLimit;
        }
        
        foreach ($clients as &$client) {
            if ($client['id'] === $clientId) {
                $client['email'] = $email;
                $client['totalGB'] = $totalGB;
                $client['expiryTime'] = $expiryTime > 0 ? $expiryTime * 1000 : 0;
                break;
            }
        }
        
        $updateInboundData = [
            'id' => $inboundId,
            'remark' => $inbound['remark'],
            'port' => $inbound['port'],
            'protocol' => $inbound['protocol'],
            'settings' => json_encode([
                'clients' => $clients,
                'decryption' => $inbound['protocol'] === 'vless' ? 'none' : null,
                'fallbacks' => []
            ]),
            'streamSettings' => $inbound['streamSettings'] ?? '{}',
            'sniffing' => $inbound['sniffing'] ?? '{}',
            'tag' => $inbound['tag'] ?? ''
        ];
        
        return $this->request('/xui/API/inbounds/update/' . $inboundId, 'POST', $updateInboundData);
    }
    
    /**
     * دریافت آمار کاربر (ترافیک مصرفی از پنل)
     * @param int $inboundId
     * @param string $clientId ایمیل یا UUID کلاینت
     * @return array|null مثلاً ['obj' => ['up'=>..., 'down'=>...]] یا ['up'=>..., 'down'=>...]
     */
    public function getClientStats($inboundId, $clientId) {
        $encoded = rawurlencode($clientId);
        $endpoints = [
            '/xui/API/inbounds/getClientTrafficsById/' . $encoded,
            '/xui/API/inbounds/getClientTraffics/' . $encoded,
            "/xui/API/inbounds/$inboundId/clientTraffics/$encoded",
            "/panel/inbound/$inboundId/clientTraffics/$encoded",
            '/xui/API/inbounds/getClientTrafficsById/' . $clientId,
            '/xui/API/inbounds/getClientTraffics/' . $clientId,
        ];
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'GET', null, false);
                if ($result && (is_array($result) && (isset($result['obj']) || isset($result['up']) || isset($result['down'])))) {
                    return $result;
                }
            } catch (Exception $e) {
                continue;
            }
        }
        try {
            return $this->request('/xui/API/inbounds/getClientTrafficsById/' . $encoded, 'GET');
        } catch (Exception $e) {
            return null;
        }
    }
    
    /**
     * دریافت ترافیک کل پنل
     */
    public function getPanelTotalTraffic() {
        $inbounds = $this->getInbounds();
        $total_traffic = 0;
        
        // بررسی ساختارهای مختلف پاسخ
        $inbound_list = [];
        if (isset($inbounds['obj']) && is_array($inbounds['obj'])) {
            $inbound_list = $inbounds['obj'];
        } elseif (isset($inbounds['data']) && is_array($inbounds['data'])) {
            $inbound_list = $inbounds['data'];
        } elseif (is_array($inbounds)) {
            $inbound_list = $inbounds;
        }
        
        foreach ($inbound_list as $inbound) {
            // محاسبه ترافیک از کلاینت‌ها
            if (isset($inbound['clients']) && is_array($inbound['clients'])) {
                foreach ($inbound['clients'] as $client) {
                    // اگر ترافیک مستقیم در کلاینت موجود باشد
                    if (isset($client['up']) && isset($client['down'])) {
                        $total_traffic += ($client['up'] ?? 0) + ($client['down'] ?? 0);
                    }
                }
            }
            
            // اگر ترافیک در inbound موجود باشد
            if (isset($inbound['up']) && isset($inbound['down'])) {
                $total_traffic += ($inbound['up'] ?? 0) + ($inbound['down'] ?? 0);
            }
        }
        
        return [
            'total' => $total_traffic,
            'inbounds_count' => count($inbound_list)
        ];
    }
    
    /**
     * تولید UUID
     */
    private function generateUUID() {
        return sprintf(
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            mt_rand(0, 0xffff), mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0x0fff) | 0x4000,
            mt_rand(0, 0x3fff) | 0x8000,
            mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
        );
    }
    
    /**
     * دریافت لینک اشتراک‌گذاری
     * در 3x-ui فرمت صحیح: /sub/{subId} (subId کلاینت، نه inboundId)
     * اگر subId داده شود از آن استفاده می‌شود؛ وگرنه از inboundId (برای سازگاری با کاربران قدیمی)
     */
    public function getSubscriptionLink($inboundId, $clientId = null, $subId = null) {
        // 3x-ui از subId کلاینت برای subscription استفاده می‌کند، نه inboundId
        $subIdentifier = !empty($subId) ? $subId : $inboundId;
        
        if (empty($subIdentifier)) {
            throw new Exception("شناسه subscription (subId یا inboundId) خالی است");
        }
        
        // تعیین base URL برای subscription
        if ($this->subscription_base_url) {
            $base = rtrim($this->subscription_base_url, '/');
            if (substr($base, -4) === '/sub') {
                $link = $base . "/$subIdentifier";
            } else {
                $link = $base . "/sub/$subIdentifier";
            }
        } elseif ($this->base_path) {
            $base = rtrim($this->base_url, '/');
            $path = rtrim($this->base_path, '/');
            if (substr($path, -4) === '/sub') {
                $link = $base . $path . "/$subIdentifier";
            } else {
                $link = $base . $path . "/sub/$subIdentifier";
            }
        } else {
            $base = rtrim($this->base_url, '/');
            $link = $base . "/sub/$subIdentifier";
        }
        
        error_log("Generated subscription link: subId=" . ($subId ?? 'N/A') . ", inboundId=$inboundId, link=$link");
        
        return $link;
    }
    
    /**
     * تنظیم آدرس پایه subscription
     */
    public function setSubscriptionBaseUrl($url) {
        $this->subscription_base_url = $url ? rtrim($url, '/') : null;
    }
    
    /**
     * دریافت لینک QR Code (برای تولید QR code از لینک subscription)
     */
    public function getQRCodeData($inboundId, $clientId) {
        return $this->getSubscriptionLink($inboundId, $clientId);
    }
    
    /**
     * ساخت لینک کانفیگ مستقیم (vless://, vmess://, trojan://, ss://)
     * @param int $inboundId
     * @param string $clientId
     * @param array $overrides تنظیمات اختصاصی برای این اینباند (address, port, host, path, sni)
     */
    public function getConfigLink($inboundId, $clientId, $overrides = []) {
        error_log("getConfigLink called: inboundId=$inboundId, clientId=$clientId, overrides=" . json_encode($overrides));
        
        // دریافت اطلاعات inbound و client
        $inbounds = $this->getInbounds();
        $inbound_list = [];
        if (isset($inbounds['obj']) && is_array($inbounds['obj'])) {
            $inbound_list = $inbounds['obj'];
        } elseif (isset($inbounds['data']) && is_array($inbounds['data'])) {
            $inbound_list = $inbounds['data'];
        } elseif (is_array($inbounds)) {
            $inbound_list = $inbounds;
        }
        
        error_log("Found " . count($inbound_list) . " inbounds");
        
        $inbound = null;
        $client = null;
        $clientIdNorm = is_string($clientId) ? trim($clientId) : (string)$clientId;
        
        foreach ($inbound_list as $inb) {
            // تطبیق id به صورت نوع-امن (رشته/عدد)
            if ((string)($inb['id'] ?? '') !== (string)$inboundId) {
                continue;
            }
            $inbound = $inb;
            // در 3x-ui گاهی clients داخل settings به صورت JSON است
            $clients = $inb['clients'] ?? [];
            if (empty($clients) && !empty($inb['settings'])) {
                $settings = is_string($inb['settings']) ? json_decode($inb['settings'], true) : $inb['settings'];
                $clients = isset($settings['clients']) && is_array($settings['clients']) ? $settings['clients'] : [];
                error_log("getConfigLink: clients loaded from inbound settings, count=" . count($clients));
            }
            error_log("Found inbound $inboundId with " . count($clients) . " clients");
            foreach ($clients as $c) {
                $cId = trim((string)($c['id'] ?? ''));
                $cEmail = trim((string)($c['email'] ?? ''));
                error_log("Checking client: id=$cId, email=$cEmail, against clientId=$clientIdNorm");
                if ($cId !== '' && ($cId === $clientIdNorm || $cId === (string)$clientId)) {
                    $client = $c;
                    error_log("Client matched by id!");
                    break;
                }
                if ($cEmail !== '' && ($cEmail === $clientIdNorm || strcasecmp($cEmail, $clientIdNorm) === 0)) {
                    $client = $c;
                    error_log("Client matched by email!");
                    break;
                }
            }
            break;
        }
        
        if (!$inbound) {
            error_log("Inbound $inboundId not found in " . count($inbound_list) . " inbounds");
            throw new Exception("Inbound با ID $inboundId یافت نشد");
        }
        
        if (!$client) {
            error_log("Client $clientIdNorm not found in inbound $inboundId (clients count was " . count($clients ?? []) . ")");
            $clientsCount = isset($clients) ? count($clients) : 0;
            throw new Exception("Client با شناسه «$clientIdNorm» در این inbound یافت نشد (تعداد کلاینت‌ها: $clientsCount). مطمئن شوید کاربر در پنل ساخته شده و email یا UUID با دیتابیس یکی است.");
        }
        
        $protocol = strtolower($inbound['protocol'] ?? '');
        $port = $inbound['port'] ?? 0;

        // نامی که در انتهای لینک بعد از # می‌آید:
        $clientEmail  = trim((string)($client['email'] ?? ''));
        $inboundRemark = trim((string)($inbound['remark'] ?? ''));
        $tagParts = [];
        if ($inboundRemark !== '' && $clientEmail !== '') {
            $tagParts[] = $inboundRemark . '-' . $clientEmail;
        } elseif ($inboundRemark !== '') {
            $tagParts[] = $inboundRemark;
        } elseif ($clientEmail !== '') {
            $tagParts[] = $clientEmail;
        }        
        $remark = trim(implode(' | ', $tagParts));
        if ($remark === '') $remark = 'user';
        
        // استخراج streamSettings
        $streamSettings = is_string($inbound['streamSettings'] ?? '{}') ? 
            json_decode($inbound['streamSettings'], true) : 
            ($inbound['streamSettings'] ?? []);
        
        $network = $streamSettings['network'] ?? 'tcp';
        $security = $streamSettings['security'] ?? 'none';
        
        // استخراج tls/reality settings
        $tlsSettings = $streamSettings['tlsSettings'] ?? [];
        $realitySettings = $streamSettings['realitySettings'] ?? [];
        $sni = $tlsSettings['serverName'] ?? '';
        $isReality = ($security === 'reality');
        $alpn = isset($tlsSettings['alpn']) ? (is_array($tlsSettings['alpn']) ? implode(',', $tlsSettings['alpn']) : $tlsSettings['alpn']) : '';

        // استخراج wsSettings
        $wsSettings = $streamSettings['wsSettings'] ?? [];
        $wsPath = $wsSettings['path'] ?? '/';
        $wsHost = '';
        if (isset($wsSettings['headers']['Host'])) $wsHost = $wsSettings['headers']['Host'];
        elseif (isset($wsSettings['headers']['host'])) $wsHost = $wsSettings['headers']['host'];
        
        // استخراج tcpSettings
        $tcpSettings = $streamSettings['tcpSettings'] ?? [];
        $headerType = $tcpSettings['header']['type'] ?? 'none';
        if ($headerType === 'http') {
            $h = $tcpSettings['header']['request']['headers']['Host'] ?? $tcpSettings['header']['request']['headers']['host'] ?? '';
            if (is_array($h)) $wsHost = $h[0] ?? '';
            else $wsHost = $h;
        }

        // استخراج grpcSettings
        $grpcSettings = $streamSettings['grpcSettings'] ?? [];
        $grpcServiceName = $grpcSettings['serviceName'] ?? '';
        
        // استخراج xtlsSettings
        $xtlsSettings = $streamSettings['xtlsSettings'] ?? [];
        $xtlsServerName = $xtlsSettings['serverName'] ?? '';
        $flow = $client['flow'] ?? '';
        
        // ذخیره میزبان اصلی پنل
        $url_parts = parse_url($this->base_url);
        $original_panel_host = $url_parts['host'] ?? '';

        // استخراج External Proxy
        $externalProxy = $streamSettings['externalProxy'] ?? [];
        $extHost = '';
        $extPort = 0;
        if (!empty($externalProxy) && is_array($externalProxy)) {
            $firstProxy = reset($externalProxy);
            if ($firstProxy) {
                $extHost = $firstProxy['dest'] ?? '';
                $extPort = (int)($firstProxy['port'] ?? 0);
            }
        }

        // آدرس نهایی لینک (Address)
        $host = $original_panel_host;
        $is_tunneled = false;
        if (!empty($overrides['address'])) {
            $host = $overrides['address'];
            $is_tunneled = true;
        } elseif (!empty($extHost)) {
            $host = $extHost;
            $is_tunneled = true;
        } elseif (!empty($this->config_domain)) {
            $host = $this->config_domain;
            $is_tunneled = true;
        } elseif ($isReality) {
            $realitySni = $realitySettings['serverNames'][0] ?? '';
            if (!empty($realitySni)) $host = $realitySni;
        } elseif (!empty($sni)) {
            $host = $sni;
        } elseif (!empty($wsHost)) {
            $host = $wsHost;
        }

        // پورت نهایی
        if (!empty($overrides['port'])) $port = (int)$overrides['port'];
        elseif ($extPort > 0) $port = $extPort;

        // اگر لینک تانل شده است ولی wsHost (هدر Host) خالی است، آن را با دامین اصلی پنل یا SNI پر کن
        if ($is_tunneled && empty($wsHost)) {
            $wsHost = !empty($sni) ? $sni : $original_panel_host;
        }

        // اعمال Override نهایی (بالاترین اولویت)
        if (!empty($overrides['host'])) $wsHost = $overrides['host'];
        if (!empty($overrides['path'])) {
            $wsPath = $overrides['path'];
            $grpcServiceName = $overrides['path'];
        }
        if (!empty($overrides['sni'])) {
            $sni = $overrides['sni'];
            if ($isReality) $realitySettings['serverNames'] = [$overrides['sni']];
        }
        
        // ساخت لینک بر اساس protocol
        switch ($protocol) {
            case 'vless':
                $uuid = $client['id'] ?? '';
                if (empty($uuid)) throw new Exception("UUID کلاینت موجود نیست");
                $params = [];
                $params[] = 'type=' . $network;
                $params[] = 'encryption=none';
                $params[] = 'security=' . ($security !== 'none' ? $security : 'none');
                if ($network === 'ws') {
                    $params[] = 'path=' . urlencode($wsPath ?: '/');
                    if (!empty($wsHost)) $params[] = 'host=' . urlencode($wsHost);
                }
                if ($network === 'tcp' && $headerType === 'http') {
                    $params[] = 'headerType=http';
                    $params[] = 'path=/';
                    if (!empty($wsHost)) $params[] = 'host=' . urlencode($wsHost);
                }
                if ($security === 'xtls') {
                    if (!empty($xtlsServerName)) $host = $xtlsServerName;
                    $params[] = 'flow=' . urlencode($flow ?: 'xtls-rprx-direct');
                }
                if ($isReality) {
                    $params[] = 'fp=' . urlencode($realitySettings['settings']['fingerprint'] ?? '');
                    $params[] = 'pbk=' . urlencode($realitySettings['settings']['publicKey'] ?? '');
                    $params[] = 'sni=' . urlencode($realitySettings['serverNames'][0] ?? $sni);
                    if (!empty($flow)) $params[] = 'flow=' . urlencode($flow);
                    if (!empty($realitySettings['shortIds'][0])) $params[] = 'sid=' . urlencode($realitySettings['shortIds'][0]);
                    if (!empty($realitySettings['settings']['spiderX'])) $params[] = 'spx=' . urlencode($realitySettings['settings']['spiderX']);
                } else {
                    if (!empty($sni)) $params[] = 'sni=' . urlencode($sni);
                    if (!empty($alpn)) $params[] = 'alpn=' . urlencode($alpn);
                }
                if ($network === 'grpc') {
                    if (!empty($grpcServiceName)) $params[] = 'serviceName=' . urlencode($grpcServiceName);
                }
                $paramsStr = implode('&', $params);
                $configLink = "vless://$uuid@$host:$port?$paramsStr#" . urlencode($remark);
                break;
                
            case 'vmess':
                $uuid = $client['id'] ?? '';
                if (empty($uuid)) throw new Exception("UUID کلاینت موجود نیست");
                $vmessConfig = [
                    'v' => '2', 'ps' => $remark, 'add' => $host, 'port' => (string)$port, 'id' => $uuid,
                    'aid' => '0', 'scy' => 'auto', 'net' => $network, 'type' => $headerType,
                    'host' => $wsHost, 'path' => $wsPath, 'tls' => ($security !== 'none' ? $security : '')
                ];
                if ($network === 'grpc') {
                    $vmessConfig['path'] = $grpcServiceName;
                    if (!empty($alpn)) $vmessConfig['alpn'] = $alpn;
                }
                if (!empty($sni)) $vmessConfig['sni'] = $sni;
                $configLink = 'vmess://' . base64_encode(json_encode($vmessConfig, JSON_UNESCAPED_SLASHES));
                break;
                
            case 'trojan':
                $password = $client['password'] ?? $client['id'] ?? '';
                if (empty($password)) throw new Exception("Password کلاینت موجود نیست");
                $params = ['type=' . $network, 'security=' . ($security !== 'none' ? $security : 'none')];
                if ($headerType === 'http') {
                    $params[] = 'path=/';
                    if (!empty($wsHost)) $params[] = 'host=' . urlencode($wsHost);
                    $params[] = 'headerType=http';
                }
                if ($network === 'ws') {
                    $params[] = 'path=' . urlencode($wsPath ?: '/');
                    if (!empty($wsHost)) $params[] = 'host=' . urlencode($wsHost);
                }
                if ($network === 'grpc') {
                    if (!empty($grpcServiceName)) $params[] = 'serviceName=' . urlencode($grpcServiceName);
                }
                if (!empty($sni)) $params[] = 'sni=' . urlencode($sni);
                if (!empty($alpn)) $params[] = 'alpn=' . urlencode($alpn);
                $configLink = "trojan://$password@$host:$port?" . implode('&', $params) . "#" . urlencode($remark);
                break;
                
            case 'shadowsocks':
            case 'ss':
                $inboundSettings = is_string($inbound['settings'] ?? '{}') ? json_decode($inbound['settings'], true) : ($inbound['settings'] ?? []);
                $method = $inboundSettings['method'] ?? 'aes-256-gcm';
                $password = $client['password'] ?? '';
                if (empty($password)) throw new Exception("Password کلاینت موجود نیست");
                $configLink = 'ss://' . base64_encode("$method:$password") . "@$host:$port#" . urlencode($remark);
                break;
                
            default:
                throw new Exception("Protocol پشتیبانی نشده: $protocol");
        }
        error_log("Generated config link: protocol=$protocol, link=" . substr($configLink, 0, 100) . "...");
        return $configLink;
    }
    
    /**
     * افزودن Inbound جدید
     */
    public function addInbound($remark, $port, $protocol, $settings, $streamSettings = '{}', $sniffing = '{}', $tag = '') {
        // بر اساس مستندات: POST /xui/API/inbounds/add
        $data = [
            'remark' => $remark,
            'port' => $port,
            'protocol' => $protocol,
            'settings' => is_string($settings) ? $settings : json_encode($settings),
            'streamSettings' => is_string($streamSettings) ? $streamSettings : json_encode($streamSettings),
            'sniffing' => is_string($sniffing) ? $sniffing : json_encode($sniffing),
            'tag' => $tag
        ];
        
        $endpoints = [
            '/xui/API/inbounds/add',
            '/xui/inbound/add',
            '/panel/inbound/add'
        ];
        
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'POST', $data, false);
                if ($result && (isset($result['success']) || isset($result['obj']))) {
                    return $result;
                }
            } catch (Exception $e) {
                continue;
            }
        }
        
        return $this->request('/xui/API/inbounds/add', 'POST', $data);
    }
    
    /**
     * حذف Inbound
     */
    public function deleteInbound($inboundId) {
        // بر اساس مستندات: POST /xui/API/inbounds/del/:id
        $endpoints = [
            '/xui/API/inbounds/del/' . $inboundId,
            '/xui/inbound/del/' . $inboundId,
            '/panel/inbound/del/' . $inboundId
        ];
        
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'POST', null, false);
                if ($result) {
                    return $result;
                }
            } catch (Exception $e) {
                continue;
            }
        }
        
        return $this->request('/xui/API/inbounds/del/' . $inboundId, 'POST');
    }
    
    /**
     * دریافت جزئیات یک Inbound
     */
    public function getInbound($inboundId) {
        // بر اساس مستندات: GET /xui/API/inbounds/get/:id
        $endpoints = [
            '/xui/API/inbounds/get/' . $inboundId,
            '/xui/inbound/get/' . $inboundId,
            '/panel/inbound/get/' . $inboundId
        ];
        
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'GET', null, false);
                if ($result && (isset($result['obj']) || isset($result['data']))) {
                    return isset($result['obj']) ? $result['obj'] : $result['data'];
                }
            } catch (Exception $e) {
                continue;
            }
        }
        
        // Fallback: جستجو در لیست
        $inbounds = $this->getInbounds();
        $inbound_list = isset($inbounds['obj']) ? $inbounds['obj'] : (isset($inbounds['data']) ? $inbounds['data'] : []);
        
        if (is_array($inbound_list)) {
            foreach ($inbound_list as $inbound) {
                if ($inbound['id'] == $inboundId) {
                    return $inbound;
                }
            }
        }
        return null;
    }
    
    /**
     * فعال/غیرفعال کردن Inbound
     */
    public function toggleInbound($inboundId, $enable = true) {
        $inbound = $this->getInbound($inboundId);
        if (!$inbound) {
            throw new Exception("Inbound یافت نشد");
        }
        
        $updateData = [
            'id' => $inboundId,
            'remark' => $inbound['remark'],
            'port' => $inbound['port'],
            'protocol' => $inbound['protocol'],
            'settings' => $inbound['settings'] ?? '{}',
            'streamSettings' => $inbound['streamSettings'] ?? '{}',
            'sniffing' => $inbound['sniffing'] ?? '{}',
            'tag' => $inbound['tag'] ?? '',
            'enable' => $enable
        ];
        
        return $this->request('/panel/inbound/update/' . $inboundId, 'POST', $updateData);
    }
    
    /**
     * فعال/غیرفعال کردن کلاینت
     */
    public function toggleClient($inboundId, $clientId, $enable = true) {
        // استفاده از updateClient برای تغییر enable
        $inbound = $this->getInbound($inboundId);
        if (!$inbound) {
            throw new Exception("Inbound یافت نشد");
        }
        
        // پیدا کردن کلاینت
        $client = null;
        foreach ($inbound['clients'] ?? [] as $c) {
            if ($c['id'] === $clientId) {
                $client = $c;
                break;
            }
        }
        
        if (!$client) {
            throw new Exception("کلاینت یافت نشد");
        }
        
        // استفاده از updateClient
        return $this->updateClient(
            $inboundId, 
            $clientId, 
            $client['email'] ?? '', 
            '', 
            0, 
            0
        );
    }
    
    /**
     * دریافت لیست کلاینت‌های یک Inbound
     */
    public function getInboundClients($inboundId) {
        $inbound = $this->getInbound($inboundId);
        if (!$inbound) {
            return [];
        }
        return $inbound['clients'] ?? [];
    }
    
    /**
     * دریافت آمار ترافیک یک Inbound
     */
    public function getInboundTraffic($inboundId) {
        // دریافت لیست کلاینت‌ها و ترافیک آن‌ها
        $inbound = $this->getInbound($inboundId);
        if (!$inbound) {
            throw new Exception("Inbound یافت نشد");
        }
        
        $clients = $inbound['clients'] ?? [];
        $traffic_data = [];
        
        foreach ($clients as $client) {
            try {
                $client_traffic = $this->getClientStats($inboundId, $client['id'] ?? $client['email'] ?? '');
                if ($client_traffic) {
                    $traffic_data[] = [
                        'client' => $client,
                        'traffic' => $client_traffic
                    ];
                }
            } catch (Exception $e) {
                // ادامه به کلاینت بعدی
                continue;
            }
        }
        
        return [
            'inbound_id' => $inboundId,
            'clients' => $traffic_data
        ];
    }
    
    /**
     * دریافت آمار سیستم
     */
    public function getSystemStats() {
        // بر اساس مستندات: GET /xui/API/server/status
        $endpoints = [
            '/panel/api/server/status',
            '/xui/API/server/status',
            '/xui/server/status',
            '/panel/system'
        ];
        
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'GET', null, false);
                if ($result) {
                    return $result;
                }
            } catch (Exception $e) {
                continue;
            }
        }
        
        return $this->request('/panel/api/server/status');
    }
    
    /**
     * دریافت config.json کامل پنل (برای چک کانفیگ و مشاهده)
     * @return array|null config به صورت آرایه
     */
    public function getConfigJson() {
        $endpoints = [
            '/panel/api/server/getConfigJson',
            '/xui/API/server/getConfigJson',
            '/panel/server/getConfigJson'
        ];
        
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'GET', null, false);
                if ($result && isset($result['success']) && $result['success'] && isset($result['value'])) {
                    $value = $result['value'];
                    if (is_string($value)) {
                        $decoded = json_decode($value, true);
                        return $decoded !== null ? $decoded : ['raw' => $value];
                    }
                    return is_array($value) ? $value : ['raw' => $value];
                }
            } catch (Exception $e) {
                continue;
            }
        }
        throw new Exception('دریافت config ناموفق بود');
    }
    
    /**
     * دریافت تنظیمات پنل
     */
    public function getSettings() {
        $endpoints = [
            '/xui/API/setting/get',
            '/panel/setting/get'
        ];
        
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'GET', null, false);
                if ($result) {
                    return $result;
                }
            } catch (Exception $e) {
                continue;
            }
        }
        
        return $this->request('/panel/setting/get');
    }
    
    /**
     * به‌روزرسانی تنظیمات پنل
     */
    public function updateSettings($settings) {
        $endpoints = [
            '/xui/API/setting/update',
            '/panel/setting/update'
        ];
        
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'POST', $settings, false);
                if ($result) {
                    return $result;
                }
            } catch (Exception $e) {
                continue;
            }
        }
        
        return $this->request('/panel/setting/update', 'POST', $settings);
    }
    
    /**
     * دریافت QR Code برای کلاینت
     */
    public function getClientQRCode($inboundId, $clientId) {
        // ساخت لینک QR Code
        $inbound = $this->getInbound($inboundId);
        if (!$inbound) {
            throw new Exception("Inbound یافت نشد");
        }
        
        $client = null;
        foreach ($inbound['clients'] ?? [] as $c) {
            if ($c['id'] === $clientId) {
                $client = $c;
                break;
            }
        }
        
        if (!$client) {
            throw new Exception("کلاینت یافت نشد");
        }
        
        // ساخت لینک subscription
        $subscriptionLink = $this->getSubscriptionLink($inboundId, $clientId);
        
        return [
            'subscription_link' => $subscriptionLink,
            'qr_code_data' => $subscriptionLink // می‌تواند برای تولید QR Code استفاده شود
        ];
    }
    
    /**
     * دریافت لیست تمام کلاینت‌ها از تمام Inbound ها
     */
    public function getAllClients() {
        $inbounds = $this->getInbounds();
        $allClients = [];
        
        if (isset($inbounds['obj']) && is_array($inbounds['obj'])) {
            foreach ($inbounds['obj'] as $inbound) {
                if (isset($inbound['clients']) && is_array($inbound['clients'])) {
                    foreach ($inbound['clients'] as $client) {
                        $client['inbound_id'] = $inbound['id'];
                        $client['inbound_remark'] = $inbound['remark'] ?? '';
                        $client['inbound_port'] = $inbound['port'] ?? 0;
                        $client['inbound_protocol'] = $inbound['protocol'] ?? '';
                        $allClients[] = $client;
                    }
                }
            }
        }
        
        return $allClients;
    }
    
    /**
     * جستجو در Inbound ها و کلاینت‌ها
     */
    public function search($query) {
        // دریافت همه inbound ها و جستجو در آن‌ها
        $inbounds = $this->getInbounds();
        $inbound_list = isset($inbounds['obj']) ? $inbounds['obj'] : (isset($inbounds['data']) ? $inbounds['data'] : []);
        
        $results = [];
        $query_lower = strtolower($query);
        
        foreach ($inbound_list as $inbound) {
            // جستجو در remark
            if (isset($inbound['remark']) && stripos($inbound['remark'], $query) !== false) {
                $results[] = $inbound;
                continue;
            }
            
            // جستجو در کلاینت‌ها
            if (isset($inbound['clients']) && is_array($inbound['clients'])) {
                foreach ($inbound['clients'] as $client) {
                    if (isset($client['email']) && stripos($client['email'], $query) !== false) {
                        $results[] = $inbound;
                        break;
                    }
                }
            }
        }
        
        return [
            'query' => $query,
            'results' => $results,
            'count' => count($results)
        ];
    }
    
    /**
     * ریست کردن ترافیک یک کلاینت
     */
    public function resetClientTraffic($inboundId, $clientId) {
        // بر اساس مستندات: POST /xui/API/inbounds/:id/resetClientTraffic/:email
        // اما clientId می‌تواند id یا email باشد
        $endpoints = [
            '/xui/API/inbounds/' . $inboundId . '/resetClientTraffic/' . $clientId,
            '/xui/inbound/' . $inboundId . '/resetClientTraffic/' . $clientId,
            '/panel/inbound/' . $inboundId . '/resetClientTraffic/' . $clientId
        ];
        
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'POST', null, false);
                if ($result) {
                    return $result;
                }
            } catch (Exception $e) {
                continue;
            }
        }
        
        return $this->request('/xui/API/inbounds/' . $inboundId . '/resetClientTraffic/' . $clientId, 'POST');
    }
    
    /**
     * ریست کردن ترافیک یک Inbound
     */
    public function resetInboundTraffic($inboundId) {
        // بر اساس مستندات: POST /xui/API/inbounds/resetAllClientTraffics/:id
        $endpoints = [
            '/xui/API/inbounds/resetAllClientTraffics/' . $inboundId,
            '/xui/inbound/resetAllClientTraffics/' . $inboundId,
            '/panel/inbound/' . $inboundId . '/resetAllClientTraffics'
        ];
        
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'POST', null, false);
                if ($result) {
                    return $result;
                }
            } catch (Exception $e) {
                continue;
            }
        }
        
        return $this->request('/xui/API/inbounds/resetAllClientTraffics/' . $inboundId, 'POST');
    }
    
    /**
     * ریست کردن ترافیک کل پنل
     */
    public function resetAllTraffic() {
        // بر اساس مستندات: POST /xui/API/inbounds/resetAllTraffics
        $endpoints = [
            '/xui/API/inbounds/resetAllTraffics',
            '/xui/inbound/resetAllTraffics',
            '/panel/inbound/resetAllTraffics'
        ];
        
        foreach ($endpoints as $endpoint) {
            try {
                $result = $this->tryRequest($endpoint, 'POST', null, false);
                if ($result) {
                    return $result;
                }
            } catch (Exception $e) {
                continue;
            }
        }
        
        return $this->request('/xui/API/inbounds/resetAllTraffics', 'POST');
    }
    
    /**
     * دریافت آمار ترافیک کل پنل (به صورت دقیق‌تر)
     */
    public function getDetailedPanelTraffic() {
        $inbounds = $this->getInbounds();
        $result = [
            'total_up' => 0,
            'total_down' => 0,
            'total' => 0,
            'inbounds' => []
        ];
        
        if (isset($inbounds['obj']) && is_array($inbounds['obj'])) {
            foreach ($inbounds['obj'] as $inbound) {
                $inbound_up = 0;
                $inbound_down = 0;
                
                // محاسبه ترافیک از کلاینت‌ها
                if (isset($inbound['clients']) && is_array($inbound['clients'])) {
                    foreach ($inbound['clients'] as $client) {
                        $inbound_up += $client['up'] ?? 0;
                        $inbound_down += $client['down'] ?? 0;
                    }
                }
                
                // اگر ترافیک در inbound موجود باشد
                $inbound_up += $inbound['up'] ?? 0;
                $inbound_down += $inbound['down'] ?? 0;
                
                $result['total_up'] += $inbound_up;
                $result['total_down'] += $inbound_down;
                $result['inbounds'][] = [
                    'id' => $inbound['id'],
                    'remark' => $inbound['remark'] ?? '',
                    'port' => $inbound['port'] ?? 0,
                    'protocol' => $inbound['protocol'] ?? '',
                    'up' => $inbound_up,
                    'down' => $inbound_down,
                    'total' => $inbound_up + $inbound_down,
                    'clients_count' => isset($inbound['clients']) ? count($inbound['clients']) : 0
                ];
            }
        }
        
        $result['total'] = $result['total_up'] + $result['total_down'];
        
        return $result;
    }
}
?>
