<?php

namespace App\Livewire\Terminal;

use App\Models\HostCredential;
use App\Models\Server;
use App\Models\TerminalSession;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Livewire\Attributes\Layout;
use Livewire\Attributes\On;
use Livewire\Component;

/**
 * Terminal page. Lists the targets (the Clusev host + each server) and, on selection, mints a
 * single-use session token that the xterm.js island uses to open a WebSocket to the terminal
 * sidecar. The component itself never touches the SSH connection or credentials — it only authorizes
 * the operator and hands out a short-lived token (resolved server-side over the internal network).
 */
#[Layout('layouts.app')]
class Index extends Component
{
    /** Show the server search box once there is more than one server to filter between. */
    private const SEARCH_THRESHOLD = 1;

    /** 'host' or a server uuid — drives the active-target highlight in the rail. */
    public string $activeKey = '';

    /** Live filter over the server rail (only shown past SEARCH_THRESHOLD servers). */
    public string $search = '';

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

    /** Mint a single-use token for the chosen target and hand it to the xterm island via an event. */
    public function open(string $kind, ?string $uuid = null): void
    {
        // Split by target: the host shell is a real login into the Clusev machine (admin-only),
        // while a server shell is a day-to-day operator action.
        abort_unless(auth()->user()?->can($kind === 'host' ? 'manage-fleet' : 'operate'), 403);

        $serverId = null;
        $label = __('terminal.host_label');
        $this->activeKey = 'host';

        if ($kind === 'host') {
            // The host terminal is a real SSH login into the Clusev machine. With no login configured
            // yet, open the setup form instead of minting a dead session.
            if (HostCredential::current() === null) {
                $this->configureHost();

                return;
            }
        } else {
            $kind = 'server';
            $server = Server::where('uuid', $uuid)->first();
            if ($server === null) {
                return;
            }
            $serverId = $server->id;
            $label = $server->name;
            $this->activeKey = $server->uuid;
        }

        $token = Str::random(48);
        TerminalSession::create([
            'token' => $token,
            'user_id' => Auth::id(),
            'kind' => $kind,
            'server_id' => $serverId,
            'expires_at' => now()->addSeconds(60),
            'created_at' => now(),
        ]);

        $this->dispatch('terminal-open', token: $token, title: $label);
    }

    /** Open the modal that stores the Clusev host SSH login. */
    public function configureHost(): void
    {
        // Configuring the host SSH login is a fleet-admin action (HostShell::mount re-gates too).
        abort_unless(auth()->user()?->can('manage-fleet'), 403);

        $this->dispatch('openModal', component: 'modals.host-shell');
    }

    /** The host-login modal saved/removed — re-render so the tile reflects the new state. */
    #[On('hostShellSaved')]
    public function hostShellSaved(): void
    {
        // No state to set; the empty handler simply triggers a re-render.
    }

    public function render()
    {
        $serverCount = Server::count();

        $servers = Server::query()
            ->when($this->search !== '', fn ($q) => $q->where('name', 'like', '%'.$this->search.'%'))
            ->orderBy('name')
            ->get(['uuid', 'name', 'status']);

        $host = HostCredential::current();

        return view('livewire.terminal.index', [
            'servers' => $servers,
            'serverCount' => $serverCount,
            'showSearch' => $serverCount > self::SEARCH_THRESHOLD,
            'hostConfigured' => $host !== null,
            // The host is always the local machine, so the tile shows just the login user.
            'hostTarget' => $host?->username,
        ])->title($this->title());
    }
}
