<?php

namespace App\Livewire\Posture;

use App\Models\Server;
use App\Services\PostureService;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\Layout;
use Livewire\Component;
use Throwable;

/**
 * Fleet security-posture scoreboard. Scores each active-credential server from its hardening state
 * (PostureService). Reading a server's posture exposes its hardening GAPS, so the page is
 * `operate`-gated (route + mount). The scan is lazy (wire:init) and per-server guarded — one
 * unreachable host shows an error row instead of blocking the whole scan.
 */
#[Layout('layouts.app')]
class Index extends Component
{
    /** @var array<string, array<string, mixed>> server uuid → score result (or ['error' => msg]) */
    public array $scores = [];

    public bool $ready = false;

    public function mount(): void
    {
        abort_unless(Auth::user()?->can('operate'), 403);
    }

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

    public function scan(PostureService $posture): void
    {
        abort_unless(Auth::user()?->can('operate'), 403);

        $this->scores = [];
        foreach (Server::withActiveCredential()->orderBy('name')->get() as $server) {
            try {
                $this->scores[$server->uuid] = $posture->score($server);
            } catch (Throwable $e) {
                $this->scores[$server->uuid] = ['error' => $e->getMessage()];
            }
        }

        $this->ready = true;
    }

    public function render(): View
    {
        $scored = array_filter($this->scores, fn ($s) => isset($s['score']));
        $avg = $scored !== [] ? (int) round(array_sum(array_column($scored, 'score')) / count($scored)) : null;

        return view('livewire.posture.index', [
            'servers' => Server::withActiveCredential()->orderBy('name')->get(['uuid', 'name']),
            'average' => $avg,
        ])->title($this->title());
    }
}
