<?php

namespace App\Support\Ssh;

use App\Models\Server;
use phpseclib3\Net\SFTP as SftpProtocol; // aliased: class names are case-insensitive, "Sftp" == "SFTP"
use RuntimeException;

/**
 * Thin wrapper around phpseclib SFTP for the file manager.
 */
class Sftp
{
    use VerifiesHostKey;

    private ?SftpProtocol $sftp = null;

    public function __construct(private readonly CredentialVault $vault) {}

    public function connect(Server $server): self
    {
        $sftp = new SftpProtocol($server->ip, $server->ssh_port ?: 22);

        $this->verifyHostKey($sftp, $server);

        $login = $this->vault->resolve($server);

        if (! $sftp->login($login['username'], $login['secret'])) {
            throw new RuntimeException("SFTP-Login auf {$server->name} fehlgeschlagen.");
        }

        $this->sftp = $sftp;

        return $this;
    }

    /**
     * @return array<int, array{name: string, type: string, size: int, permissions: string, mtime: int}>
     */
    public function list(string $path = '.'): array
    {
        $raw = $this->client()->rawlist($path) ?: [];
        $entries = [];

        foreach ($raw as $name => $info) {
            if ($name === '.' || $name === '..') {
                continue;
            }

            $entries[] = [
                'name' => (string) $name,
                'type' => ($info['type'] ?? null) === SftpProtocol::TYPE_DIRECTORY ? 'dir' : 'file',
                'size' => (int) ($info['size'] ?? 0),
                'permissions' => isset($info['permissions'])
                    ? substr(sprintf('%o', $info['permissions']), -4)
                    : '',
                'mtime' => (int) ($info['mtime'] ?? 0),
            ];
        }

        return $entries;
    }

    public function get(string $remote): string
    {
        return (string) $this->client()->get($remote);
    }

    public function put(string $remote, string $contents): bool
    {
        return $this->client()->put($remote, $contents);
    }

    public function delete(string $remote): bool
    {
        return $this->client()->delete($remote);
    }

    public function putFromFile(string $remote, string $localPath): bool
    {
        return $this->client()->put($remote, $localPath, SftpProtocol::SOURCE_LOCAL_FILE);
    }

    public function size(string $remote): int
    {
        return (int) ($this->client()->filesize($remote) ?: 0);
    }

    public function disconnect(): void
    {
        $this->sftp?->disconnect();
        $this->sftp = null;
    }

    private function client(): SftpProtocol
    {
        return $this->sftp ?? throw new RuntimeException('Nicht verbunden — connect() zuerst aufrufen.');
    }
}
