<?php

namespace App\Livewire\Modals;

use App\Models\AuditEvent;
use App\Models\HostCredential;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use LivewireUI\Modal\ModalComponent;

/**
 * Form modal: configure the SSH login for the "Clusev host" terminal. The target is ALWAYS the local
 * machine Clusev runs on (the Docker host, reached at host.docker.internal — fixed, not a free-form
 * host, because an arbitrary machine is what fleet servers are for). The operator only supplies the
 * login (user + password/key, and the port). Stores a single encrypted HostCredential; on edit a
 * blank secret keeps the stored one, and the clear-text secret is never echoed back into the form.
 */
class HostShell extends ModalComponent
{
    /** The host terminal always targets the local machine; not operator-editable. */
    private const HOST = 'host.docker.internal';

    public int $port = 22;

    public string $username = 'root';

    public string $authType = 'password';

    public string $secret = '';

    public string $passphrase = '';

    /** True when a HostCredential already exists — then a blank secret keeps the stored one. */
    public bool $configured = false;

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

    public function mount(): void
    {
        // The host SSH login is the Clusev machine itself — the most dangerous credential.
        abort_unless(auth()->user()?->can('manage-fleet'), 403);

        $hc = HostCredential::current();
        if ($hc !== null) {
            $this->configured = true;
            $this->port = $hc->port;
            $this->username = $hc->username;
            $this->authType = $hc->auth_type;
            // secret/passphrase stay blank — never reveal the stored login back into the form.
        }
    }

    /**
     * @return array<string, array<int, mixed>>
     */
    protected function rules(): array
    {
        return [
            'port' => ['required', 'integer', 'min:1', 'max:65535'],
            'username' => ['required', 'string', 'max:120'],
            'authType' => ['required', Rule::in(['password', 'key'])],
            // On first setup the secret is required; when editing an existing login a blank
            // secret means "keep the stored one".
            'secret' => [$this->configured ? 'nullable' : 'required', 'string'],
            'passphrase' => ['nullable', 'string'],
        ];
    }

    /**
     * @return array<string, string>
     */
    protected function validationAttributes(): array
    {
        return [
            'port' => __('terminal.host_field_port'),
            'username' => __('terminal.host_field_user'),
            'authType' => __('terminal.host_field_auth'),
            'secret' => $this->authType === 'key' ? __('terminal.host_field_key') : __('terminal.host_field_password'),
        ];
    }

    public function save(): void
    {
        // Re-gate on save: refuse a demoted user / hand-crafted /livewire/update after mount.
        abort_unless(auth()->user()?->can('manage-fleet'), 403);

        $data = $this->validate();

        $existing = HostCredential::current();
        // Switching the auth method (password ↔ key) requires a fresh secret: keeping the stored one
        // would feed e.g. an old password into key auth (or vice versa) and silently break the login.
        if ($this->secret === '' && $existing !== null && $this->authType !== $existing->auth_type) {
            throw ValidationException::withMessages([
                'secret' => __('terminal.host_secret_required_for_auth_change'),
            ]);
        }

        $hc = $existing ?? new HostCredential;
        $hc->host = self::HOST; // always the local machine — not operator-editable
        $hc->port = (int) $data['port'];
        $hc->username = trim($data['username']);
        $hc->auth_type = $data['authType'] === 'key' ? 'key' : 'password';
        // Only overwrite the secret when a new one was entered (blank = keep the stored one).
        if ($this->secret !== '') {
            $hc->secret = $this->secret;
            $hc->passphrase = $this->authType === 'key' && $this->passphrase !== '' ? $this->passphrase : null;
        }
        $hc->save();

        AuditEvent::create([
            'user_id' => Auth::id(),
            'actor' => Auth::user()?->name ?? 'system',
            'action' => 'host.terminal.configure',
            'target' => $hc->username.'@'.$hc->host.':'.$hc->port,
            'ip' => request()->ip(),
        ]);

        $this->dispatch('hostShellSaved');
        $this->dispatch('notify', message: __('terminal.host_saved'));
        $this->closeModal();
    }

    public function remove(): void
    {
        // Re-gate on remove: refuse a demoted user / hand-crafted /livewire/update after mount.
        abort_unless(auth()->user()?->can('manage-fleet'), 403);

        $hc = HostCredential::current();
        if ($hc !== null) {
            $target = $hc->username.'@'.$hc->host.':'.$hc->port;
            HostCredential::query()->delete(); // singleton — clear any/all rows, not just the first
            AuditEvent::create([
                'user_id' => Auth::id(),
                'actor' => Auth::user()?->name ?? 'system',
                'action' => 'host.terminal.remove',
                'target' => $target,
                'ip' => request()->ip(),
            ]);
        }

        $this->dispatch('hostShellSaved');
        $this->dispatch('notify', message: __('terminal.host_removed'));
        $this->closeModal();
    }

    public function render()
    {
        return view('livewire.modals.host-shell');
    }
}
