<?php

namespace App\Livewire\Modals;

use App\Models\AuditEvent;
use App\Models\Server;
use App\Services\MaintenanceService;
use Illuminate\Support\Facades\Auth;
use LivewireUI\Modal\ModalComponent;
use Throwable;

/**
 * Apply pending package updates across apt/dnf/zypper (resolved per OS). mount()
 * reads whether updates are supported on this host + the pending count; the body
 * explains in GERMAN what happens — never a shell command. „Jetzt aktualisieren"
 * runs the upgrade, AUDITs, notifies, and keeps the modal open showing a clean
 * German result. Unsupported hosts (e.g. Arch/Alpine) get a German reason instead.
 */
class SystemUpdate extends ModalComponent
{
    public int $serverId;

    public string $serverName = '';

    /** True when Clusev can manage package updates on this host. */
    public bool $supported = false;

    /** German reason shown when updates are not supported on this OS. */
    public ?string $unsupportedReason = null;

    /** Pending upgrade count, or null when it could not be determined ("unbekannt"). */
    public ?int $pending = null;

    public bool $done = false;

    public bool $ok = false;

    /** Trimmed package-manager output, only shown when the upgrade failed. */
    public string $output = '';

    public ?string $error = null;

    public function mount(int $serverId, MaintenanceService $maintenance): void
    {
        // Applying apt/dnf/zypper package updates on a managed host over SSH is a fleet-admin action.
        abort_unless(auth()->user()?->can('manage-fleet'), 403);

        $this->serverId = $serverId;

        $server = Server::find($serverId);
        if (! $server) {
            $this->error = __('common.server_not_found');

            return;
        }

        $this->serverName = $server->name;

        try {
            $this->unsupportedReason = $maintenance->updateSupport($server);
            $this->supported = $this->unsupportedReason === null;
            $this->pending = $this->supported ? $maintenance->pendingUpdates($server) : null;
        } catch (Throwable $e) {
            $this->error = $e->getMessage();
        }
    }

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

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

        if ($this->error !== null || $this->done || ! $this->supported) {
            return;
        }

        $server = Server::find($this->serverId);
        if (! $server) {
            $this->error = __('common.server_not_found');

            return;
        }

        try {
            $result = $maintenance->applyUpgrades($server);
        } catch (Throwable $e) {
            $this->done = true;
            $this->ok = false;
            $this->output = $e->getMessage();
            $this->dispatch('notify', message: __('modals.system_update.notify_failed'));

            return;
        }

        $this->done = true;
        $this->ok = $result['ok'];
        // On success we surface a clean German line, not the raw apt log; on failure
        // the trimmed error tail helps the operator diagnose.
        $this->output = $this->ok ? '' : $result['output'];

        AuditEvent::create([
            'user_id' => Auth::id(),
            'server_id' => $server->id,
            'actor' => Auth::user()?->name ?? 'system',
            'action' => 'system.package_upgrade',
            'target' => $server->name.($this->ok ? '' : ' '.__('modals.system_update.audit_failed_suffix')),
            'ip' => request()->ip(),
        ]);

        if ($this->ok) {
            $this->pending = 0;
            $this->dispatch('notify', message: __('modals.system_update.notify_updated'));
        } else {
            $this->dispatch('notify', message: __('modals.system_update.notify_failed'));
        }
    }

    public function render()
    {
        return view('livewire.modals.system-update');
    }
}
