<?php

/**
 * 3x-ui panel configuration (v3 API only — /panel/api/*).
 */
class XuiV3Config
{
    /** @var array<string, mixed> */
    private static $globals = [];

    public static function loadGlobals(): void
    {
        if (!empty(self::$globals)) {
            return;
        }

        $localFile = dirname(__DIR__) . '/xui-env.local.php';
        if (file_exists($localFile)) {
            require $localFile;
        }

        self::$globals = [
            'auth_mode' => strtolower((string) self::env('XUI_AUTH_MODE', $GLOBALS['XUI_AUTH_MODE'] ?? 'auto')),
            'base_url' => rtrim((string) self::env('XUI_BASE_URL', $GLOBALS['XUI_BASE_URL'] ?? ''), '/'),
            'api_token' => (string) self::env('XUI_API_TOKEN', $GLOBALS['XUI_API_TOKEN'] ?? ''),
            'username' => (string) self::env('XUI_USERNAME', $GLOBALS['XUI_USERNAME'] ?? ''),
            'password' => (string) self::env('XUI_PASSWORD', $GLOBALS['XUI_PASSWORD'] ?? ''),
            'subscription_base_url' => rtrim((string) self::env('XUI_SUBSCRIPTION_BASE_URL', $GLOBALS['XUI_SUBSCRIPTION_BASE_URL'] ?? ''), '/'),
            'sub_path' => self::normalizePath((string) self::env('XUI_SUB_PATH', $GLOBALS['XUI_SUB_PATH'] ?? '/sub/')),
            'json_sub_path' => self::normalizePath((string) self::env('XUI_JSON_SUB_PATH', $GLOBALS['XUI_JSON_SUB_PATH'] ?? '/json/')),
            'clash_sub_path' => self::normalizePath((string) self::env('XUI_CLASH_SUB_PATH', $GLOBALS['XUI_CLASH_SUB_PATH'] ?? '/clash/')),
            'timeout_seconds' => max(10, (int) self::env('XUI_HTTP_TIMEOUT_SECONDS', $GLOBALS['XUI_HTTP_TIMEOUT_SECONDS'] ?? 60)),
            'connect_timeout_seconds' => max(5, (int) self::env('XUI_HTTP_CONNECT_TIMEOUT_SECONDS', $GLOBALS['XUI_HTTP_CONNECT_TIMEOUT_SECONDS'] ?? 20)),
        ];
    }

    /** @return array<string, mixed> */
    public static function getGlobals(): array
    {
        self::loadGlobals();
        return self::$globals;
    }

    /** @param array<string, mixed> $serverInfo */
    public static function forServer(array $serverInfo): array
    {
        self::loadGlobals();

        $panelUrl = self::normalizePanelUrl(rtrim((string) ($serverInfo['panel_url'] ?? ''), '/'));
        $baseUrl = !empty($serverInfo['xui_base_url'])
            ? self::normalizePanelUrl(rtrim((string) $serverInfo['xui_base_url'], '/'))
            : ($panelUrl !== '' ? $panelUrl : self::normalizePanelUrl(self::$globals['base_url']));

        $apiToken = trim(!empty($serverInfo['api_token'])
            ? (string) $serverInfo['api_token']
            : self::$globals['api_token']);

        $subBase = !empty($serverInfo['subscription_base_url'])
            ? rtrim((string) $serverInfo['subscription_base_url'], '/')
            : (self::$globals['subscription_base_url'] !== '' ? self::$globals['subscription_base_url'] : $baseUrl);

        return [
            'auth_mode' => self::$globals['auth_mode'],
            'base_url' => $baseUrl,
            'api_token' => $apiToken,
            'username' => !empty($serverInfo['username']) ? (string) $serverInfo['username'] : self::$globals['username'],
            'password' => !empty($serverInfo['password']) ? (string) $serverInfo['password'] : self::$globals['password'],
            'subscription_base_url' => $subBase,
            'sub_path' => self::$globals['sub_path'],
            'json_sub_path' => self::$globals['json_sub_path'],
            'clash_sub_path' => self::$globals['clash_sub_path'],
            'timeout_seconds' => self::$globals['timeout_seconds'],
            'connect_timeout_seconds' => self::$globals['connect_timeout_seconds'],
        ];
    }

    /**
     * Build config for connection probe (add-server flow before DB row exists).
     * @return array<string, mixed>
     */
    public static function forProbe(string $panelUrl, string $username, string $password, string $apiToken = ''): array
    {
        self::loadGlobals();
        $token = trim($apiToken !== '' ? $apiToken : self::$globals['api_token']);

        return [
            'auth_mode' => self::$globals['auth_mode'],
            'base_url' => self::normalizePanelUrl($panelUrl),
            'api_token' => $token,
            'username' => $username,
            'password' => $password,
            'subscription_base_url' => self::$globals['subscription_base_url'] ?: self::normalizePanelUrl($panelUrl),
            'sub_path' => self::$globals['sub_path'],
            'json_sub_path' => self::$globals['json_sub_path'],
            'clash_sub_path' => self::$globals['clash_sub_path'],
            'timeout_seconds' => self::$globals['timeout_seconds'],
            'connect_timeout_seconds' => self::$globals['connect_timeout_seconds'],
        ];
    }

    public static function isPanelServer(array $serverInfo): bool
    {
        return ($serverInfo['type'] ?? '') === 'sanaei';
    }

    /**
     * Normalize panel root URL (webBasePath). Strips trailing /panel — API paths add /panel/api/*.
     */
    public static function normalizePanelUrl(string $url): string
    {
        $url = trim($url);
        if ($url === '') {
            return '';
        }
        $url = rtrim($url, '/');
        if (preg_match('#(/panel)(/api)?$#i', $url)) {
            $url = preg_replace('#(/panel)(/api)?$#i', '', $url);
            $url = rtrim($url, '/');
        }
        return $url;
    }

    /**
     * Test panel reachability with v3 API (Bearer or JSON cookie login + CSRF).
     * @return array{success:bool,msg:string,detail?:string,http_code?:int}
     */
    public static function probeConnection(string $panelUrl, string $username, string $password, string $apiToken = ''): array
    {
        require_once __DIR__ . '/XuiV3ApiClient.php';

        $normalized = self::normalizePanelUrl($panelUrl);
        if ($normalized === '') {
            return ['success' => false, 'msg' => 'آدرس پنل خالی است'];
        }

        $token = trim($apiToken);
        self::loadGlobals();
        $authMode = self::$globals['auth_mode'];

        $attempts = [];
        if ($token !== '' && $authMode !== 'cookie') {
            $attempts[] = self::forProbe($normalized, $username, $password, $token);
        }
        if ($username !== '' && $password !== '' && ($authMode !== 'token' && $authMode !== 'bearer')) {
            $cookieCfg = self::forProbe($normalized, $username, $password, '');
            $cookieCfg['auth_mode'] = 'cookie';
            $attempts[] = $cookieCfg;
        }
        if (empty($attempts)) {
            $attempts[] = self::forProbe($normalized, $username, $password, $token);
        }

        $lastFail = null;
        foreach ($attempts as $cfg) {
            $result = self::probeWithConfig($cfg);
            if ($result['success']) {
                return $result;
            }
            $lastFail = $result;
        }

        return $lastFail ?? ['success' => false, 'msg' => 'Connection failed'];
    }

    /**
     * @param array<string, mixed> $cfg
     * @return array{success:bool,msg:string,detail?:string,http_code?:int}
     */
    private static function probeWithConfig(array $cfg): array
    {
        $client = new XuiV3ApiClient($cfg);
        $normalized = (string) $cfg['base_url'];

        $status = $client->getServerStatus();
        if ($status->success) {
            $via = !empty($cfg['api_token']) && ($cfg['auth_mode'] ?? '') !== 'cookie' ? 'Bearer Token' : 'Login';
            return [
                'success' => true,
                'msg' => (string) ($status->msg ?: 'OK'),
                'detail' => $via . ' — ' . $normalized,
            ];
        }

        $options = $client->getInboundOptions();
        if ($options->success) {
            $count = is_array($options->obj) ? count($options->obj) : 0;
            return [
                'success' => true,
                'msg' => 'Connected',
                'detail' => $count . ' inbound — ' . $normalized,
            ];
        }

        $list = $client->listInbounds();
        if ($list->success) {
            $count = is_array($list->obj) ? count($list->obj) : 0;
            return [
                'success' => true,
                'msg' => 'Connected',
                'detail' => $count . ' inbound — ' . $normalized,
            ];
        }

        $httpCode = (int) ($status->http_code ?: $options->http_code ?: $list->http_code ?: 0);
        $msg = (string) ($status->msg ?: $options->msg ?: $list->msg ?: $client->getLastAuthError() ?: 'Connection failed');
        $detail = 'HTTP ' . $httpCode . ' — ' . $normalized;
        if ($httpCode === 404) {
            $detail .= ' — آدرس را بدون /panel بزنید';
        } elseif ($httpCode === 403) {
            $detail .= ' — Token از Settings → Security → API Tokens';
        }

        return ['success' => false, 'msg' => $msg, 'detail' => $detail, 'http_code' => $httpCode];
    }

    private static function env(string $key, $default = '')
    {
        $value = getenv($key);
        return $value !== false ? $value : $default;
    }

    private static function normalizePath(string $path): string
    {
        if ($path === '') {
            return '/sub/';
        }
        if ($path[0] !== '/') {
            $path = '/' . $path;
        }
        if (substr($path, -1) !== '/') {
            $path .= '/';
        }
        return $path;
    }
}
