<?php

namespace App\Livewire\Modals;

use App\Models\AuditEvent;
use App\Models\Server;
use App\Services\FleetService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use LivewireUI\Modal\ModalComponent;

/**
 * Form modal: register a new server in the fleet and deposit its SSH access in
 * the encrypted vault. The server starts as 'offline'; the first poll/connect
 * promotes it. The SSH port lives on the servers table (column ssh_port), which
 * is what the SSH layer (SshClient/Sftp) reads.
 */
class CreateServer extends ModalComponent
{
    public string $name = '';

    public string $ip = '';

    public int $sshPort = 22;

    public string $username = '';

    public string $authType = 'password';

    public string $secret = '';

    public string $passphrase = '';

    public string $credentialName = '';

    public function mount(): void
    {
        // Registering a server + depositing its SSH login is a fleet-admin action.
        abort_unless(auth()->user()?->can('manage-fleet'), 403);
    }

    public static function modalMaxWidth(): string
    {
        return 'lg';
    }

    /**
     * @return array<string, array<int, mixed>>
     */
    protected function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:120'],
            // Accept either a real IP (validated by PHP's filter) or a valid DNS hostname.
            'ip' => ['required', 'string', 'max:255', function ($attribute, $value, $fail) {
                $value = (string) $value;
                $isIp = filter_var($value, FILTER_VALIDATE_IP) !== false;
                // A digits-and-dots string is an IPv4 attempt — it must be a VALID IP,
                // not a numeric "hostname" (so 999.999.999.999 is rejected).
                $numericDotted = preg_match('/^[0-9.]+$/', $value) === 1;
                $isHost = ! $numericDotted && preg_match('/^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)*[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/', $value) === 1;
                if (! $isIp && ! $isHost) {
                    $fail(__('modals.create_server.validation_ip_or_host'));
                }
            }],
            'sshPort' => ['required', 'integer', 'min:1', 'max:65535'],
            'username' => ['required', 'string', 'max:120'],
            'authType' => ['required', Rule::in(['password', 'key'])],
            'secret' => ['required', 'string'],
            'passphrase' => ['nullable', 'string'],
            'credentialName' => ['nullable', 'string', 'max:120'],
        ];
    }

    /**
     * @return array<string, string>
     */
    protected function validationAttributes(): array
    {
        return [
            'name' => __('modals.create_server.attr_name'),
            'ip' => __('modals.create_server.attr_ip'),
            'sshPort' => __('modals.create_server.attr_ssh_port'),
            'username' => __('modals.create_server.attr_user'),
            'authType' => __('modals.create_server.attr_auth'),
            'secret' => $this->authType === 'key' ? __('modals.create_server.attr_secret_key') : __('modals.create_server.attr_secret_password'),
            'credentialName' => __('modals.create_server.attr_credential_name'),
        ];
    }

    public function save(): void
    {
        // Re-gate on save: mount()'s guard is not enough — a demotion mid-session or a
        // hand-crafted /livewire/update must still be refused at the write.
        abort_unless(auth()->user()?->can('manage-fleet'), 403);

        $data = $this->validate();

        // Atomic: create server + credential, then VERIFY the SSH login. A failed
        // probe throws -> the whole transaction rolls back, so no half-registered
        // server is left behind, and the operator sees the real SSH reason on the
        // form. A verified server starts as 'pending' ("Initialisierung") — never
        // red — until the first contact promotes it.
        $server = DB::transaction(function () use ($data) {
            $server = Server::create([
                'name' => trim($data['name']),
                'ip' => trim($data['ip']),
                'ssh_port' => (int) $data['sshPort'],
                'status' => 'pending',
            ]);

            $server->credential()->create([
                'name' => trim($this->credentialName) !== '' ? trim($this->credentialName) : null,
                'username' => trim($data['username']),
                'auth_type' => $data['authType'] === 'key' ? 'key' : 'password',
                'secret' => $this->secret,
                'passphrase' => $this->authType === 'key' && $this->passphrase !== '' ? $this->passphrase : null,
            ]);

            $server->load('credential');
            $probe = app(FleetService::class)->testConnection($server);
            if (! $probe['ok']) {
                throw ValidationException::withMessages([
                    'secret' => __('modals.create_server.validation_ssh_failed', ['error' => Str::limit((string) $probe['error'], 120)]),
                ]);
            }

            AuditEvent::create([
                'user_id' => Auth::id(),
                'server_id' => $server->id,
                'actor' => Auth::user()?->name ?? 'system',
                'action' => 'server.create',
                'target' => $server->name.' · '.$server->ip,
                'ip' => request()->ip(),
            ]);

            return $server;
        });

        $this->dispatch('serverCreated');
        $this->dispatch('notify', message: __('modals.create_server.notify_added'));
        $this->closeModal();
    }

    public function render()
    {
        return view('livewire.modals.create-server');
    }
}
