<?php

namespace App\Livewire\Modals;

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

/**
 * Tune the fail2ban [DEFAULT] policy (Sperrdauer / Max. Fehlversuche / Zeitfenster)
 * via a small German form — no shell commands are ever shown. mount() loads the
 * current values; save() clamps + validates the integers, writes jail.local, AUDITs,
 * notifies and closes.
 */
class Fail2banConfig extends ModalComponent
{
    public int $serverId;

    public string $serverName = '';

    /** fail2ban duration grammar (e.g. 600, 10m, 1h 30m, -1 = dauerhaft) — kept verbatim. */
    public string $bantime = '10m';

    public int $maxretry = 5;

    public string $findtime = '10m';

    public ?string $error = null;

    /** True only once the current policy was read — guards save() from overwriting with unseen defaults. */
    public bool $loaded = false;

    public function mount(int $serverId, Fail2banService $fail2ban): void
    {
        abort_unless(auth()->user()?->can('manage-network'), 403);

        $this->serverId = $serverId;

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

            return;
        }

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

        try {
            $current = $fail2ban->readConfig($server);
            $this->bantime = $current['bantime'];
            $this->maxretry = $current['maxretry'];
            $this->findtime = $current['findtime'];
            $this->loaded = true;
        } catch (Throwable $e) {
            $this->error = $e->getMessage();
        }
    }

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

    public function save(Fail2banService $fail2ban): void
    {
        abort_unless(auth()->user()?->can('manage-network'), 403);

        // Never overwrite the remote policy with defaults the operator never saw —
        // saving is only allowed once the current values were read successfully.
        if (! $this->loaded) {
            $this->error = __('modals.fail2ban_config.error_read_locked');

            return;
        }

        $this->error = null;

        // fail2ban duration grammar: a number (seconds) optionally with a unit
        // (s/m/h/d/w/mo/y), space-separated composites, or -1 for a permanent ban.
        $duration = ['required', 'string', 'max:40', 'regex:/^(?:-1|\d+(?:\.\d+)?\s*(?:s|m|h|d|w|mo|y)?(?:\s+\d+(?:\.\d+)?\s*(?:s|m|h|d|w|mo|y)?)*)$/i'];

        $validated = $this->validate([
            'bantime' => $duration,
            'maxretry' => ['required', 'integer', 'min:1', 'max:100'],
            'findtime' => $duration,
        ], [
            'bantime.regex' => __('modals.fail2ban_config.error_bantime_invalid'),
            'findtime.regex' => __('modals.fail2ban_config.error_findtime_invalid'),
        ], [
            'bantime' => __('modals.fail2ban_config.attr_bantime'),
            'maxretry' => __('modals.fail2ban_config.attr_maxretry'),
            'findtime' => __('modals.fail2ban_config.attr_findtime'),
        ]);

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

            return;
        }

        try {
            // Tuning lives in its OWN drop-in (separate from the whitelist), so this can
            // never touch ignoreip. Values were validated/clamped above.
            $result = $fail2ban->writeTuning(
                $server,
                (string) $validated['bantime'],
                (int) $validated['maxretry'],
                (string) $validated['findtime'],
            );
        } catch (Throwable $e) {
            $this->error = $e->getMessage();

            return;
        }

        if (! $result['ok']) {
            $this->error = __('modals.fail2ban_config.error_save_failed', ['output' => $result['output']]);

            return;
        }

        AuditEvent::create([
            'user_id' => Auth::id(),
            'server_id' => $server->id,
            'actor' => Auth::user()?->name ?? 'system',
            'action' => 'fail2ban.configure',
            'target' => $server->name.' · bantime '.$validated['bantime'].', maxretry '.$validated['maxretry'],
            'ip' => request()->ip(),
        ]);

        $this->dispatch('hardeningApplied');
        $this->dispatch('notify', message: __('modals.fail2ban_config.notify_saved'));
        $this->closeModal();
    }

    public function render()
    {
        return view('livewire.modals.fail2ban-config');
    }
}
