<?php

require_once '/opt/DragonCore/config.php';

function createXrayTable()
{
    global $db_user, $db_pass;
    $conn = pg_connect("host=localhost dbname=dragoncore user={$db_user} password={$db_pass}");
    if (!$conn) {
        echo "Failed to connect to PostgreSQL\n";
        return;
    }
    $query = "CREATE TABLE IF NOT EXISTS xray (
        id       SERIAL PRIMARY KEY,
        uuid     TEXT,
        nick     TEXT,
        expiry   DATE,
        protocol TEXT
    );";
    $result = pg_query($conn, $query);
    if (!$result) {
        echo "Error creating Xray table: " . pg_last_error($conn) . "\n";
    }
    pg_close($conn);
}

function xrayConfigPath(): string
{
    return '/usr/local/etc/xray/config.json';
}

function xrayEnsureConfigDir(): void
{
    $dir = dirname(xrayConfigPath());
    if (!is_dir($dir)) {
        mkdir($dir, 0755, true);
    }
}

function isXrayInstalled(): bool
{
    $paths = [
        '/usr/local/bin/xray',
        '/usr/bin/xray'
    ];
    foreach ($paths as $p) {
        if (file_exists($p)) {
            return true;
        }
    }
    return false;
}

function xrayInstall()
{
    if (isXrayInstalled()) {
        echo "echo 'Xray ja esta instalado';\n";
        return;
    }

    echo 'bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install' . "\n";
}

function xrayRemove()
{
    if (!isXrayInstalled()) {
        echo "echo 'Xray nao esta instalado';\n";
        return;
    }

    echo 'bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ remove' . "\n";
}

function xrayGetProtocols()
{
    global $db_user, $db_pass;
    $protocols = [];
    $conn = pg_connect("host=localhost dbname=dragoncore user={$db_user} password={$db_pass}");
    if ($conn) {
        $res = pg_query($conn, 'SELECT DISTINCT protocol FROM xray ORDER BY protocol');
        while ($row = pg_fetch_assoc($res)) {
            $protocols[] = $row['protocol'];
        }
        pg_close($conn);
    }
    echo implode(', ', $protocols);
}

function xrayListSimple()
{
    global $db_user, $db_pass;
    $conn = pg_connect("host=localhost dbname=dragoncore user={$db_user} password={$db_pass}");
    if ($conn) {
        $res = pg_query($conn, 'SELECT uuid, expiry FROM xray ORDER BY id');
        while ($row = pg_fetch_assoc($res)) {
            echo $row['uuid'] . '  ' . $row['expiry'] . "\n";
        }
        pg_close($conn);
    }
}

function xrayChoice(): string
{
    return isXrayInstalled() ? 'Remover' : 'Instalar';
}

function xrayUuid(): string
{
    $uuid = trim((string)@shell_exec('uuidgen'));
    if ($uuid !== '') {
        return $uuid;
    }

    if (function_exists('random_bytes')) {
        $data = random_bytes(16);
        $data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
        $data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
        return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
    }

    return md5(uniqid((string)mt_rand(), true));
}

function xrayGenerateConfig($port, string $network = 'xhttp')
{
    $configPath = xrayConfigPath();
    xrayEnsureConfigDir();

    $port = (int)$port;
    if ($port <= 0 || $port > 65535) {
        $port = 443;
    }

    $network = strtolower($network);
    if (!in_array($network, ['xhttp', 'ws', 'grpc', 'tcp'], true)) {
        $network = 'xhttp';
    }


    $apiInbound = [
        'tag'      => 'api',
        'port'     => 1080,
        'protocol' => 'dokodemo-door',
        'settings' => [
            'address' => '127.0.0.1',
        ],
        'listen'   => '127.0.0.1',
    ];


    $streamSettings = [
        'network' => $network,
    ];

    if ($network === 'xhttp') {
        $streamSettings['security'] = 'tls';
        $streamSettings['tlsSettings'] = [
            'certificates' => [[
                'certificateFile' => '/opt/DragonCore/ssl/fullchain.pem',
                'keyFile'        => '/opt/DragonCore/ssl/privkey.pem',
            ]],
            'alpn' => ['http/1.1'],
        ];
        $streamSettings['xhttpSettings'] = [
            'headers'              => null,
            'host'                 => '',
            'mode'                 => '',
            'noSSEHeader'          => false,
            'path'                 => '/',
            'scMaxBufferedPosts'   => 30,
            'scMaxEachPostBytes'   => '1000000',
            'scStreamUpServerSecs' => '20-80',
            'xPaddingBytes'        => '100-1000',
        ];
    } elseif ($network === 'ws') {
        $streamSettings['security'] = 'none';
        $streamSettings['wsSettings'] = [
            'acceptProxyProtocol' => false,
            'headers'             => (object)[],
            'heartbeatPeriod'     => 0,
            'host'                => '',
            'path'                => '/',
        ];
    } elseif ($network === 'grpc') {
        $streamSettings['security'] = 'none';
        $streamSettings['grpcSettings'] = [
            'serviceName'           => 'vlessgrpc',
            'multiMode'             => false,
            'idle_timeout'          => 60,
            'permit_without_stream' => false,
        ];
    } else {
        $streamSettings['security'] = 'none';
    }


    $dragonInbound = [
        'tag'      => 'inbound-dragoncore',
        'port'     => $port,
        'protocol' => 'vless',
        'settings' => [
            'clients'    => [],
            'decryption' => 'none',
            'fallbacks'  => [],
        ],
        'streamSettings' => $streamSettings,
    ];

    $config = [
        'api' => [
            'services' => [
                'HandlerService',
                'LoggerService',
                'StatsService',
            ],
            'tag' => 'api',
        ],
        'burstObservatory' => null,
        'dns'              => null,
        'fakedns'          => null,
        'inbounds'         => [
            $apiInbound,
            $dragonInbound,
        ],
        'log' => [
            'access'      => '/var/log/v2ray/access.log',
            'dnsLog'      => false,
            'error'       => '',
            'loglevel'    => 'info',
            'maskAddress' => '',
        ],
        'observatory' => null,
        'outbounds' => [
            [
                'protocol' => 'freedom',
                'settings' => (object)[],
                'tag'      => 'direct',
            ],
            [
                'protocol' => 'blackhole',
                'settings' => (object)[],
                'tag'      => 'blocked',
            ],
        ],
        'policy' => [
            'levels' => [
                '0' => [
                    'statsUserDownlink' => true,
                    'statsUserUplink'   => true,
                ],
            ],
            'system' => [
                'statsInboundDownlink'  => true,
                'statsInboundUplink'    => true,
                'statsOutboundDownlink' => false,
                'statsOutboundUplink'   => false,
            ],
        ],
        'reverse' => null,
        'routing' => [
            'domainStrategy' => 'AsIs',
            'rules' => [
                [
                    'inboundTag'  => ['api'],
                    'outboundTag' => 'api',
                    'type'        => 'field',
                ],
                [
                    'ip'          => ['geoip:private'],
                    'outboundTag' => 'blocked',
                    'type'        => 'field',
                ],
                [
                    'outboundTag' => 'blocked',
                    'protocol'    => ['bittorrent'],
                    'type'        => 'field',
                ],
            ],
        ],
        'stats'     => (object)[],
        'transport' => null,
    ];

    file_put_contents(
        $configPath,
        json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
    );

    @shell_exec('systemctl restart xray');
    echo "Config Xray gerado em {$configPath} (porta {$port}, protocolo {$network})\n";
}

function xrayFixEmptyObjects(array &$config): void
{

    if (isset($config['stats']) && is_array($config['stats']) && empty($config['stats'])) {
        $config['stats'] = (object)[];
    }


    if (isset($config['outbounds']) && is_array($config['outbounds'])) {
        foreach ($config['outbounds'] as &$out) {
            if (isset($out['settings']) && is_array($out['settings']) && empty($out['settings'])) {
                $out['settings'] = (object)[];
            }
        }
        unset($out);
    }
}

function xrayAddUser(string $nick, string $protocol = 'xhttp')
{
    global $db_user, $db_pass;

    $uuid   = xrayUuid();
    $expiry = date('Y-m-d', strtotime('+30 days'));

    $configPath = xrayConfigPath();
    if (!file_exists($configPath)) {
        echo "Arquivo de configuracao {$configPath} nao encontrado. Gere o config Xray primeiro.\n";
        return;
    }

    $config = json_decode(file_get_contents($configPath), true);
    if (!is_array($config)) {
        echo "Falha ao ler/decodificar {$configPath}.\n";
        return;
    }


    $inboundIndex = null;
    if (isset($config['inbounds']) && is_array($config['inbounds'])) {
        foreach ($config['inbounds'] as $idx => $in) {
            if (isset($in['tag']) && $in['tag'] === 'inbound-dragoncore') {
                $inboundIndex = $idx;
                break;
            }
        }
    }

    if ($inboundIndex === null) {
        echo "Inbound 'inbound-dragoncore' nao encontrado no config Xray.\n";
        return;
    }

    if (
        !isset($config['inbounds'][$inboundIndex]['settings']['clients']) ||
        !is_array($config['inbounds'][$inboundIndex]['settings']['clients'])
    ) {
        $config['inbounds'][$inboundIndex]['settings']['clients'] = [];
    }

    $client = [
        'id'    => $uuid,
        'email' => $nick,
        'level' => 0,
    ];
    $config['inbounds'][$inboundIndex]['settings']['clients'][] = $client;

    xrayFixEmptyObjects($config);
    file_put_contents(
        $configPath,
        json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
    );


    $conn = pg_connect("host=localhost dbname=dragoncore user={$db_user} password={$db_pass}");
    if ($conn) {
        $query = "INSERT INTO xray (uuid, nick, expiry, protocol) VALUES ($1,$2,$3,$4)";
        $stmt  = pg_prepare($conn, '', $query);
        pg_execute($conn, '', [$uuid, $nick, $expiry, $protocol]);
        pg_close($conn);
    }

    @shell_exec('systemctl restart xray');

    $domain = trim((string)shell_exec('hostname -I | awk "{print $1}"'));
    if ($domain === '') {
        $domain = '127.0.0.1';
    }
    $port = $config['inbounds'][$inboundIndex]['port'];
    $uri  = "vless://{$uuid}@{$domain}:{$port}#{$nick}";

    echo "UUID Criado: {$uuid}\n{$uri}\n";
}

function xrayRemoveUser($identifier)
{
    global $db_user, $db_pass;
    $uuid = null;

    if (is_numeric($identifier)) {
        $conn = pg_connect("host=localhost dbname=dragoncore user={$db_user} password={$db_pass}");
        if ($conn) {
            $res = pg_query_params($conn, 'SELECT uuid FROM xray WHERE id = $1', [$identifier]);
            if ($res && ($row = pg_fetch_assoc($res))) {
                $uuid = $row['uuid'];
            }
            pg_close($conn);
        }
    } else {
        $uuid = $identifier;
    }

    if (!$uuid) {
        echo "UUID nao encontrado\n";
        return;
    }

    $configPath = xrayConfigPath();
    if (file_exists($configPath)) {
        $config = json_decode(file_get_contents($configPath), true);
        if (isset($config['inbounds']) && is_array($config['inbounds'])) {
            $inboundIndex = null;
            foreach ($config['inbounds'] as $idx => $in) {
                if (isset($in['tag']) && $in['tag'] === 'inbound-dragoncore') {
                    $inboundIndex = $idx;
                    break;
                }
            }

            if (
                $inboundIndex !== null &&
                isset($config['inbounds'][$inboundIndex]['settings']['clients']) &&
                is_array($config['inbounds'][$inboundIndex]['settings']['clients'])
            ) {

                $clients = &$config['inbounds'][$inboundIndex]['settings']['clients'];
                foreach ($clients as $index => $cli) {
                    if (isset($cli['id']) && $cli['id'] === $uuid) {
                        array_splice($clients, $index, 1);
                        break;
                    }
                }

                xrayFixEmptyObjects($config);
                file_put_contents(
                    $configPath,
                    json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
                );
            }
        }
    }

    $conn = pg_connect("host=localhost dbname=dragoncore user={$db_user} password={$db_pass}");
    if ($conn) {
        pg_query_params($conn, 'DELETE FROM xray WHERE uuid = $1', [$uuid]);
        pg_close($conn);
    }

    @shell_exec('systemctl restart xray');
    echo "Usuario removido: {$uuid}\n";
}

function xrayListUsers()
{
    global $db_user, $db_pass;
    $conn = pg_connect("host=localhost dbname=dragoncore user={$db_user} password={$db_pass}");
    if (!$conn) {
        echo "Falha ao conectar ao banco de dados\n";
        return;
    }
    $res = pg_query($conn, 'SELECT id, uuid, nick, expiry, protocol FROM xray ORDER BY id');
    while ($row = pg_fetch_assoc($res)) {
        echo "ID: {$row['id']} | NICK: {$row['nick']} | UUID: {$row['uuid']} | EXPIRA: {$row['expiry']} | PROTO: {$row['protocol']}\n";
    }
    pg_close($conn);
}

function xrayCert(string $domain)
{
    $sslDir  = '/opt/DragonCore/ssl';
    $keyFile = $sslDir . '/privkey.pem';
    $crtFile = $sslDir . '/fullchain.pem';

    if (!is_dir($sslDir)) {
        mkdir($sslDir, 0700, true);
    }

    $subject = '/C=BR/ST=SP/L=SaoPaulo/O=DragonCore/OU=VPN/CN=' . $domain;

    $cmd  = 'openssl req -x509 -nodes -newkey rsa:2048 '
        . '-days 9999 '
        . '-subj ' . escapeshellarg($subject) . ' '
        . '-keyout ' . escapeshellarg($keyFile) . ' '
        . '-out ' . escapeshellarg($crtFile) . ' 2>/dev/null';

    @shell_exec($cmd);

    if (file_exists($keyFile) && file_exists($crtFile)) {
        echo "Certificado TLS autoassinado (9999 dias) gerado para {$domain}\n";
    } else {
        echo "Falha ao gerar certificado autoassinado para {$domain}\n";
    }
}

function xrayPurgeExpired()
{
    global $db_user, $db_pass;
    $today = date('Y-m-d');
    $conn = pg_connect("host=localhost dbname=dragoncore user={$db_user} password={$db_pass}");
    if ($conn) {
        $res = pg_query_params($conn, 'SELECT uuid FROM xray WHERE expiry < $1', [$today]);
        while ($row = pg_fetch_assoc($res)) {
            xrayRemoveUser($row['uuid']);
        }
        pg_close($conn);
    }
}

function xrayInfo()
{
    $binary = isXrayInstalled() ? '/usr/local/bin/xray' : 'xray';
    $version = trim((string)shell_exec($binary . ' -version 2>&1'));

    global $db_user, $db_pass;
    $conn  = pg_connect("host=localhost dbname=dragoncore user={$db_user} password={$db_pass}");
    $count = 0;
    $protocols = [];
    if ($conn) {
        $res = pg_query($conn, 'SELECT COUNT(*) AS c FROM xray');
        if ($row = pg_fetch_assoc($res)) {
            $count = $row['c'];
        }
        $res2 = pg_query($conn, 'SELECT DISTINCT protocol FROM xray');
        while ($r = pg_fetch_assoc($res2)) {
            $protocols[] = $r['protocol'];
        }
        pg_close($conn);
    }

    echo "Xray Versao: {$version}\n";
    echo "Usuarios cadastrados: {$count}\n";
    echo "Protocolos em uso: " . implode(', ', $protocols) . "\n";
}
