<?php

declare(strict_types=1);

namespace ServerManager\Services;

use phpseclib3\Net\SSH2;
use phpseclib3\Crypt\PublicKeyLoader;
use ServerManager\Core\App;
use ServerManager\Core\Encryption;
use ServerManager\Core\Logger;

class SSHService
{
    private ?SSH2 $connection = null;
    private array $serverConfig;
    private Logger $logger;

    public function __construct()
    {
        $this->logger = Logger::getInstance();
    }

    public function connect(array $server, bool $forceReconnect = false): bool
    {
        if ($this->connection !== null && !$forceReconnect) {
            return $this->connection->isConnected();
        }

        $this->serverConfig = $server;
        $config = App::getConfig()['ssh'];

        try {
            $host = $server['ip_address'] ?? $server['hostname'];
            $port = (int) ($server['ssh_port'] ?? 22);

            $this->connection = new SSH2($host, $port, $config['timeout'] ?? 30);

            if (!empty($server['ssh_key'])) {
                $key = PublicKeyLoader::load($server['ssh_key']);
                $result = $this->connection->login($server['ssh_user'], $key);
            } elseif (!empty($server['ssh_password'])) {
                $result = $this->connection->login($server['ssh_user'], $server['ssh_password']);
            } else {
                throw new \RuntimeException('No authentication method configured');
            }

            if (!$result) {
                throw new \RuntimeException('SSH authentication failed for ' . $host);
            }

            if ($config['keepalive_interval'] ?? 0 > 0) {
                $this->connection->setKeepAlive($config['keepalive_interval']);
            }

            $this->logger->info("SSH connection established to {$host}");
            return true;
        } catch (\Throwable $e) {
            $this->logger->error("SSH connection failed: " . $e->getMessage(), [
                'host' => $host ?? 'unknown',
                'port' => $port ?? 22,
            ]);
            $this->connection = null;
            return false;
        }
    }

    public function disconnect(): void
    {
        if ($this->connection !== null) {
            $this->connection->disconnect();
            $this->connection = null;
        }
    }

    public function exec(string $command, int $timeout = 0): array
    {
        if ($this->connection === null || !$this->connection->isConnected()) {
            throw new \RuntimeException('No active SSH connection');
        }

        $timeout = $timeout ?: (App::getConfig()['ssh']['command_timeout'] ?? 60);

        $this->connection->setTimeout($timeout);

        $output = $this->connection->exec($command);
        $exitStatus = $this->connection->getExitStatus();
        $stderr = $this->connection->getStdError() ?? '';

        $this->logger->info("SSH command executed", [
            'command' => $command,
            'exit_status' => $exitStatus,
        ]);

        return [
            'output' => $output,
            'exit_status' => $exitStatus,
            'stderr' => $stderr,
        ];
    }

    public function write(string $data): void
    {
        if ($this->connection !== null && $this->connection->isConnected()) {
            $this->connection->write($data);
        }
    }

    public function read(string $expected = '', int $mode = SSH2::READ_REGEX): string
    {
        if ($this->connection === null || !$this->connection->isConnected()) {
            throw new \RuntimeException('No active SSH connection');
        }
        return $this->connection->read($expected, $mode);
    }

    public function getInteractiveShell(): ?SSH2
    {
        return $this->connection;
    }

    public function testConnection(array $server): bool
    {
        try {
            $result = $this->connect($server, true);
            if ($result) {
                $this->disconnect();
                return true;
            }
            return false;
        } catch (\Throwable $e) {
            $this->logger->warning("SSH connection test failed", ['error' => $e->getMessage()]);
            return false;
        }
    }

    public function getSystemInfo(): array
    {
        $commands = [
            'hostname' => 'hostname',
            'os' => 'cat /etc/os-release | head -1',
            'kernel' => 'uname -r',
            'uptime' => 'uptime -p',
            'cpu_model' => 'cat /proc/cpuinfo | grep "model name" | head -1',
            'cpu_cores' => 'nproc',
            'mem_total' => 'free -b | grep Mem | awk \'{print $2}\'',
            'mem_used' => "free -b | grep Mem | awk '{print $3}'",
            'mem_avail' => "free -b | grep Mem | awk '{print $7}'",
            'disk_total' => "df -B1 / | tail -1 | awk '{print $2}'",
            'disk_used' => "df -B1 / | tail -1 | awk '{print $3}'",
            'disk_avail' => "df -B1 / | tail -1 | awk '{print $4}'",
            'disk_percent' => "df / | tail -1 | awk '{print $5}' | sed 's/%//'",
        ];

        $info = [];
        foreach ($commands as $key => $cmd) {
            $result = $this->exec($cmd);
            $info[$key] = trim($result['output']);
        }

        return $info;
    }

    public function __destruct()
    {
        $this->disconnect();
    }
}
