<?php

namespace App\Livewire\Docker;

use App\Exceptions\DockerNotInstalled;
use App\Models\AuditEvent;
use App\Models\HostCredential;
use App\Models\Server;
use App\Services\DockerService;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Auth;
use InvalidArgumentException;
use Livewire\Attributes\Layout;
use Livewire\Component;
use Throwable;

/**
 * Docker for the CLUSEV HOST — the machine Clusev runs on, reached via the stored HostCredential
 * over the local Docker gateway. Admin-only (manage-fleet), the same bar as the host terminal: a
 * `Stop` on a control-plane container takes Clusev down. Per-SERVER container management lives on
 * the server-details page (the "Docker" tab / App\Livewire\Servers\ServerDocker). Loads lazily.
 */
#[Layout('layouts.app')]
class Index extends Component
{
    /** @var array<int, array<string, string>> */
    public array $containers = [];

    public bool $connected = false;

    public bool $ready = false;

    /** Docker binary absent on the host — an honest state, not an error. */
    public bool $notInstalled = false;

    /** No host SSH login configured yet — point the operator to setup. */
    public bool $hostNotConfigured = false;

    /** The docker error (daemon down / permission / rootless), surfaced so "empty" isn't misleading. */
    public ?string $error = null;

    public function mount(): void
    {
        // The host is the control-plane machine — admin-only, like the host terminal.
        abort_unless(Auth::user()?->can('manage-fleet'), 403);
    }

    public function title(): string
    {
        return __('docker.title');
    }

    /** A transient Server pointing at the Clusev host (built from the host login), or null if unset. */
    private function hostServer(): ?Server
    {
        return HostCredential::current()?->toServer();
    }

    public function load(DockerService $docker): void
    {
        $this->containers = [];
        $this->connected = false;
        $this->notInstalled = false;
        $this->hostNotConfigured = false;
        $this->error = null;

        $server = $this->hostServer();
        if (! $server) {
            $this->hostNotConfigured = true;
            $this->ready = true;

            return;
        }

        try {
            // ONE SSH round-trip: containers() derives the "not installed" state from the docker ps
            // call itself (a missing binary throws DockerNotInstalled), so no separate available() probe.
            $this->containers = $docker->containers($server);
            $this->connected = true;
        } catch (DockerNotInstalled) {
            $this->notInstalled = true;
        } catch (Throwable $e) {
            $this->connected = false;
            $this->error = mb_scrub(mb_strcut($e->getMessage(), 0, 300), 'UTF-8');
        }

        $this->ready = true;
    }

    public function action(string $id, string $op, DockerService $docker): void
    {
        abort_unless(Auth::user()?->can('manage-fleet'), 403);

        $server = $this->hostServer();
        if (! $server) {
            return;
        }

        try {
            $res = $docker->containerAction($server, $id, $op);
            AuditEvent::create([
                'user_id' => Auth::id(),
                'server_id' => null, // the host is transient (no fleet row); the target label names it
                'actor' => Auth::user()?->name ?? 'system',
                'action' => 'docker.action',
                'target' => "{$op} {$id} · {$server->name}",
                'ip' => request()->ip(),
            ]);
            $this->dispatch('notify', message: $res['ok']
                ? __('docker.action_ok', ['op' => $op, 'ref' => $id])
                : __('docker.action_failed', ['error' => $res['output']]));
        } catch (InvalidArgumentException) {
            $this->dispatch('notify', message: __('docker.invalid_ref'));
        }

        $this->load($docker);
    }

    /** Open the read-only logs modal for a host container (ref re-validated in DockerService). */
    public function viewLogs(string $id): void
    {
        abort_unless(Auth::user()?->can('manage-fleet'), 403);

        $this->dispatch('openModal', component: 'modals.container-logs', arguments: [
            // 0 is the sentinel the logs modal resolves to the Clusev host (no persisted id).
            'serverId' => 0,
            'ref' => $id,
        ]);
    }

    public function render(): View
    {
        return view('livewire.docker.index')->title($this->title());
    }
}
