<?php

namespace App\Livewire\Settings;

use App\Models\AuditEvent;
use App\Models\BannedIp;
use App\Models\Setting;
use App\Rules\ValidIpOrCidr;
use App\Services\BruteforceGuard;
use App\Support\Confirm\ConfirmToken;
use App\Support\Confirm\InvalidConfirmToken;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Livewire\Attributes\On;
use Livewire\Component;

class LoginProtection extends Component
{
    public bool $enabled = true;

    public int $maxretry = 10;

    public int $findtime = 10;

    public int $bantime = 60;

    public string $whitelist = '';

    public function mount(BruteforceGuard $guard): void
    {
        abort_unless(auth()->user()?->can('manage-panel'), 403);
        $this->enabled = $guard->enabled();
        $this->maxretry = $guard->maxretry();
        $this->findtime = $guard->findtime();
        $this->bantime = $guard->bantime();
        $this->whitelist = implode("\n", $guard->whitelist());
    }

    public function save(BruteforceGuard $guard): void
    {
        abort_unless(auth()->user()?->can('manage-panel'), 403);
        $this->validate([
            'maxretry' => ['required', 'integer', 'min:1', 'max:1000'],
            'findtime' => ['required', 'integer', 'min:1', 'max:1440'],
            'bantime' => ['required', 'integer', 'min:1', 'max:43200'],
            'whitelist' => ['nullable', 'string'],
        ]);

        $lines = array_values(array_filter(array_map('trim', preg_split('/[\r\n,]+/', $this->whitelist) ?: [])));
        foreach ($lines as $line) {
            if (Validator::make(['v' => $line], ['v' => [new ValidIpOrCidr]])->fails()) {
                $this->addError('whitelist', __('settings.lp_whitelist_invalid', ['value' => $line]));

                return;
            }
        }

        Setting::put('bruteforce_enabled', $this->enabled ? '1' : '0');
        Setting::put('bruteforce_maxretry', (string) $this->maxretry);
        Setting::put('bruteforce_findtime', (string) $this->findtime);
        Setting::put('bruteforce_bantime', (string) $this->bantime);
        Setting::put('bruteforce_whitelist', implode("\n", $lines));

        // Widening the whitelist immediately clears any now-exempt active bans.
        foreach (BannedIp::active()->get() as $ban) {
            if ($guard->isExempt($ban->ip)) {
                $guard->unban($ban->ip);
            }
        }

        $this->audit('auth.protection_updated', null);
        $this->dispatch('notify', message: __('settings.lp_saved'));
    }

    public function whitelistMyIp(): void
    {
        abort_unless(auth()->user()?->can('manage-panel'), 403);
        $ip = (string) request()->ip();
        $lines = array_values(array_filter(array_map('trim', preg_split('/[\r\n,]+/', $this->whitelist) ?: [])));
        if (! in_array($ip, $lines, true)) {
            $lines[] = $ip;
        }
        $this->whitelist = implode("\n", $lines);
    }

    public function confirmUnban(string $ip): void
    {
        abort_unless(auth()->user()?->can('manage-panel'), 403);
        $this->dispatch('openModal',
            component: 'modals.confirm-action',
            arguments: [
                'heading' => __('settings.lp_unban_heading'),
                'body' => __('settings.lp_unban_body', ['ip' => $ip]),
                'confirmLabel' => __('settings.lp_unban'),
                'danger' => false,
                'icon' => 'shield',
                'notify' => __('settings.lp_unban_notify'),
                'token' => ConfirmToken::issue('banCleared', ['ip' => $ip], auditTarget: $ip),
            ],
        );
    }

    #[On('banCleared')]
    public function unban(string $confirmToken, BruteforceGuard $guard): void
    {
        abort_unless(auth()->user()?->can('manage-panel'), 403);
        try {
            $payload = ConfirmToken::consume($confirmToken, 'banCleared');
        } catch (InvalidConfirmToken) {
            return;
        }
        $ip = (string) $payload['params']['ip'];
        $guard->unban($ip);
        $this->audit('auth.ip_unbanned', $ip);
    }

    public function confirmUnbanAll(): void
    {
        abort_unless(auth()->user()?->can('manage-panel'), 403);
        $this->dispatch('openModal',
            component: 'modals.confirm-action',
            arguments: [
                'heading' => __('settings.lp_unban_all_heading'),
                'body' => __('settings.lp_unban_all_body'),
                'confirmLabel' => __('settings.lp_unban_all'),
                'danger' => true,
                'icon' => 'shield',
                'notify' => __('settings.lp_unban_all_notify'),
                'token' => ConfirmToken::issue('bansClearedAll'),
            ],
        );
    }

    #[On('bansClearedAll')]
    public function unbanAll(string $confirmToken, BruteforceGuard $guard): void
    {
        abort_unless(auth()->user()?->can('manage-panel'), 403);
        try {
            ConfirmToken::consume($confirmToken, 'bansClearedAll');
        } catch (InvalidConfirmToken) {
            return; // forged / replayed / direct-bypass attempt — no-op
        }
        $guard->unbanAll();
        $this->audit('auth.ip_unbanned', 'all');
    }

    private function audit(string $action, ?string $target): void
    {
        AuditEvent::create([
            'user_id' => Auth::id(),
            'actor' => Auth::user()?->name ?? 'system',
            'action' => $action,
            'target' => $target,
            'ip' => request()->ip(),
        ]);
    }

    public function render(BruteforceGuard $guard)
    {
        $ip = (string) request()->ip();

        return view('livewire.settings.login-protection', [
            'bans' => BannedIp::query()->where('banned_until', '>', now())->orderByDesc('banned_until')->limit(200)->get(),
            'currentIp' => $ip,
            'currentIpExempt' => $guard->isExempt($ip),
            // Read-only status of the env-driven honeypot deception layer (no toggle here).
            'honeypotEnabled' => (bool) config('clusev.honeypot.enabled'),
        ]);
    }
}
