<?php

declare(strict_types=1);

namespace App\Listeners;

use App\Models\User;
use App\Services\DberpAuditService;
use Filament\Facades\Filament;
use Illuminate\Auth\Events\Failed;
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use SensitiveParameter;

/**
 * 后台登录、失败与登出写入 core_audit_logs，归类为系统操作。
 *
 * 监听器方法由 Laravel 事件自动发现注册（handleLogin/handleFailed/handleLogout），
 * 勿在 ServiceProvider 中重复 Event::listen，否则一次登录会写入两条审计。
 */
final class LogDberpAdminAuthAudit
{
    public const ACTION_LOGIN_SUCCEEDED = 'dberp.auth.login_succeeded';

    public const ACTION_LOGIN_FAILED = 'dberp.auth.login_failed';

    public const ACTION_LOGOUT = 'dberp.auth.logout';

    public function __construct(
        private readonly DberpAuditService $auditService,
    ) {}

    public function handleLogin(Login $event): void
    {
        if (! $this->shouldAudit($event) || ! $event->user instanceof User) {
            return;
        }

        $event->user->forceFill([
            'last_login_at' => now(),
        ])->saveQuietly();

        $this->auditService->log(
            self::ACTION_LOGIN_SUCCEEDED,
            $event->user,
            null,
            $this->authAuditNewValues([
                'login_account' => $event->user->login_account,
                'email' => $event->user->email,
            ]),
        );
    }

    public function handleFailed(Failed $event): void
    {
        if (! $this->shouldAudit($event)) {
            return;
        }

        $loginIdentifier = $this->extractLoginIdentifier($event->credentials);

        if ($loginIdentifier === null) {
            return;
        }

        $this->auditService->log(
            self::ACTION_LOGIN_FAILED,
            $event->user instanceof User ? $event->user : null,
            null,
            $this->authAuditNewValues([
                'login_identifier' => $loginIdentifier,
                'reason' => $event->user instanceof User ? 'invalid_password' : 'unknown_account',
            ]),
        );
    }

    public function handleLogout(Logout $event): void
    {
        if (! $this->shouldAudit($event) || ! $event->user instanceof User) {
            return;
        }

        $this->auditService->log(
            self::ACTION_LOGOUT,
            $event->user,
            null,
            $this->authAuditNewValues([
                'login_account' => $event->user->login_account,
                'email' => $event->user->email,
            ]),
        );
    }

    private function shouldAudit(Login|Failed|Logout $event): bool
    {
        if ($event->guard !== 'web') {
            return false;
        }

        if (request()?->routeIs('install.*')) {
            return false;
        }

        $panel = Filament::getCurrentPanel();

        if ($panel !== null && $panel->getId() === 'admin') {
            return true;
        }

        return request()?->routeIs('filament.admin.*') ?? false;
    }

    /**
     * @param  array<string, mixed>  $credentials
     */
    private function extractLoginIdentifier(#[SensitiveParameter] array $credentials): ?string
    {
        $identifier = trim((string) ($credentials['dberp_login_identifier'] ?? ''));

        return $identifier !== '' ? $identifier : null;
    }

    /**
     * @param  array<string, mixed>  $payload
     * @return array<string, mixed>
     */
    private function authAuditNewValues(array $payload): array
    {
        $ipAddress = request()?->ip();

        if ($ipAddress !== null && $ipAddress !== '') {
            $payload['ip_address'] = $ipAddress;
        }

        return $payload;
    }
}
