<?php

namespace App\Services;

use App\Models\Player;
use App\Models\AuditLog;
use App\Models\AdminUser;
use Illuminate\Http\Request;
use App\Http\Middleware\RecordAdminApiAudit;

/**
 * 审计日志写入入口：落到表 audit_logs，仅 created_at。
 *
 * operator_type：admin / player / system（常量见此类）。
 */
final class AuditLogger
{
    /** 后台账号，operator_id = admin_users.id */
    public const OPERATOR_ADMIN = 'admin';

    /** 终端玩家，operator_id = players.id */
    public const OPERATOR_PLAYER = 'player';

    /** 系统任务（定时任务、异步 Job 无自然人操作者时使用） */
    public const OPERATOR_SYSTEM = 'system';

    /**
     * @param  array<string, mixed>|null  $beforeJson
     * @param  array<string, mixed>|null  $afterJson
     */
    public static function record(
        string $operatorType,
        int $operatorId,
        ?string $moduleCode = null,
        ?string $actionCode = null,
        ?string $targetType = null,
        ?string $targetId = null,
        ?array $beforeJson = null,
        ?array $afterJson = null,
        ?string $ip = null,
        ?string $userAgent = null,
    ): AuditLog {
        return AuditLog::query()->create([
            'operator_type' => $operatorType,
            'operator_id' => $operatorId,
            'module_code' => $moduleCode,
            'action_code' => $actionCode,
            'target_type' => $targetType,
            'target_id' => $targetId,
            'before_json' => $beforeJson,
            'after_json' => $afterJson,
            'ip' => $ip,
            'user_agent' => self::truncateUserAgent($userAgent),
        ]);
    }

    /**
     * 从当前 HTTP 请求补全 IP、User-Agent（后台 / 玩家 API 调用方便）。
     *
     * @param  array<string, mixed>|null  $beforeJson
     * @param  array<string, mixed>|null  $afterJson
     */
    public static function recordFromRequest(
        Request $request,
        string $operatorType,
        int $operatorId,
        ?string $moduleCode = null,
        ?string $actionCode = null,
        ?string $targetType = null,
        ?string $targetId = null,
        ?array $beforeJson = null,
        ?array $afterJson = null,
    ): AuditLog {
        $row = self::record(
            $operatorType,
            $operatorId,
            $moduleCode,
            $actionCode,
            $targetType,
            $targetId,
            $beforeJson,
            $afterJson,
            $request->ip(),
            $request->userAgent(),
        );

        self::markAdminApiAuditRecorded($request);

        return $row;
    }

    /** 业务层已写审计时，避免 {@see RecordAdminApiAudit} 重复落库（FormRequest 可能与管道中的 Request 非同一实例）。 */
    public static function markAdminApiAuditRecorded(Request $request): void
    {
        $request->attributes->set(RecordAdminApiAudit::ATTRIBUTE_AUDIT_RECORDED, true);

        try {
            $resolved = request();
            if ($resolved !== $request) {
                $resolved->attributes->set(RecordAdminApiAudit::ATTRIBUTE_AUDIT_RECORDED, true);
            }
        } catch (\Throwable) {
        }
    }

    public static function recordForAdmin(AdminUser $admin, ?Request $request = null, ?string $moduleCode = null, ?string $actionCode = null, ?string $targetType = null, ?string $targetId = null, ?array $beforeJson = null, ?array $afterJson = null): AuditLog
    {
        if ($request !== null) {
            return self::recordFromRequest(
                $request,
                self::OPERATOR_ADMIN,
                (int) $admin->getKey(),
                $moduleCode,
                $actionCode,
                $targetType,
                $targetId,
                $beforeJson,
                $afterJson,
            );
        }

        return self::record(
            self::OPERATOR_ADMIN,
            (int) $admin->getKey(),
            $moduleCode,
            $actionCode,
            $targetType,
            $targetId,
            $beforeJson,
            $afterJson,
        );
    }

    public static function recordForPlayer(Player $player, ?Request $request = null, ?string $moduleCode = null, ?string $actionCode = null, ?string $targetType = null, ?string $targetId = null, ?array $beforeJson = null, ?array $afterJson = null): AuditLog
    {
        if ($request !== null) {
            return self::recordFromRequest(
                $request,
                self::OPERATOR_PLAYER,
                (int) $player->getKey(),
                $moduleCode,
                $actionCode,
                $targetType,
                $targetId,
                $beforeJson,
                $afterJson,
            );
        }

        return self::record(
            self::OPERATOR_PLAYER,
            (int) $player->getKey(),
            $moduleCode,
            $actionCode,
            $targetType,
            $targetId,
            $beforeJson,
            $afterJson,
        );
    }

    /** 定时任务或队列内无 Request 时使用。 */
    public static function recordForSystem(?string $moduleCode = null, ?string $actionCode = null, ?string $targetType = null, ?string $targetId = null, ?array $beforeJson = null, ?array $afterJson = null): AuditLog
    {
        return self::record(
            self::OPERATOR_SYSTEM,
            0,
            $moduleCode,
            $actionCode,
            $targetType,
            $targetId,
            $beforeJson,
            $afterJson,
        );
    }

    private static function truncateUserAgent(?string $userAgent): ?string
    {
        if ($userAgent === null || $userAgent === '') {
            return null;
        }

        return mb_substr($userAgent, 0, 255);
    }
}
