<?php

namespace App\Services;

use App\Models\OpenVpnServer;
use App\Models\VpnClient;
use phpseclib3\Net\SSH2;
use phpseclib3\Net\SFTP;
use phpseclib3\Crypt\PublicKeyLoader;
use RuntimeException;

class OpenVpnProvisioner
{
    protected function ssh(OpenVpnServer $s): SSH2
    {
        $ssh = new SSH2($s->host, $s->port);
        if ($s->auth_type === 'key') {
            $key = PublicKeyLoader::load(decrypt($s->private_key));
            if (!$ssh->login($s->username, $key)) throw new RuntimeException('SSH key auth failed');
        } else {
            if (!$ssh->login($s->username, decrypt($s->password))) throw new RuntimeException('SSH password auth failed');
        }
        return $ssh;
    }

    public function createClient(VpnClient $client): void
    {
        $s = $client->server;
        $ssh = $this->ssh($s);
        $client->update(['status'=>'provisioning','last_error'=>null]);

        // 1) create CN + static IP on server
        $cmd = sprintf('OVPN_ROOT=%s CCD_DIR=%s /usr/local/bin/ovpn-provision create %s %s',
            escapeshellarg($s->ovpn_root),
            escapeshellarg($s->ccd_dir),
            escapeshellarg($client->common_name),
            escapeshellarg($client->static_ip)
        );
        $out = trim($ssh->exec($cmd));
        if (strpos($out,'OK') === false) throw new RuntimeException('Provision script failed: '.$out);

        // 2) fetch OVPN bundle
        $cmd = sprintf('OVPN_ROOT=%s /usr/local/bin/ovpn-provision bundle %s',
            escapeshellarg($s->ovpn_root),
            escapeshellarg($client->common_name)
        );
        $bundle = $ssh->exec($cmd);
        if (!str_contains($bundle,'-----BEGIN-OVPN-----')) throw new RuntimeException('Bundle failed');

        // store bundle locally
        $dir = storage_path("app/vpn/{$client->id}");
        @mkdir($dir, 0775, true);
        $ovpnPath = "$dir/{$client->common_name}.ovpn";
        file_put_contents($ovpnPath, $bundle);

        $client->update([
            'ovpn_path' => $ovpnPath,
            'status'    => 'active',
        ]);
    }

    public function revoke(VpnClient $client): void
    {
        $s = $client->server;
        $ssh = $this->ssh($s);
        $cmd = sprintf('OVPN_ROOT=%s CCD_DIR=%s /usr/local/bin/ovpn-provision revoke %s',
            escapeshellarg($s->ovpn_root),
            escapeshellarg($s->ccd_dir),
            escapeshellarg($client->common_name)
        );
        $ssh->exec($cmd);
        $client->update(['status'=>'revoked']);
    }
}
