<?php

namespace Kylin\GameData\Services;

use Illuminate\Support\Facades\DB;
use Kylin\GameData\Enums\GameDataErrorCode;
use Kylin\GameData\Enums\SaveAction;
use Kylin\GameData\Exceptions\GameDataException;
use Kylin\GameData\Models\GameSave;
use Kylin\GameData\Models\GameSaveHistory;
use Kylin\Game\Models\Game;

class GameDataService
{
    public function __construct(
        private readonly GameDataSyncService $syncService,
    ) {}

    /**
     * 获取存档
     */
    public function getSave(int $userId, int $gameId): ?GameSave
    {
        return GameSave::where('user_id', $userId)
            ->where('game_id', $gameId)
            ->first();
    }

    /**
     * 保存数据
     */
    public function saveData(int $userId, int $gameId, array $data, int $version): GameSave
    {
        // 检查存档大小
        $this->assertSaveSize($data);

        return DB::transaction(function () use ($userId, $gameId, $data, $version) {
            $save = GameSave::where('user_id', $userId)
                ->where('game_id', $gameId)
                ->first();

            $checksum = $this->syncService->calculateChecksum($data);

            if ($save === null) {
                // 新建存档
                $save = GameSave::create([
                    'user_id' => $userId,
                    'game_id' => $gameId,
                    'data' => $data,
                    'version' => $version,
                    'client_version' => $version,
                    'synced_at' => now(),
                    'checksum' => $checksum,
                ]);

                // 记录历史
                $this->recordHistory($save->id, $data, $version, SaveAction::Create);

                $this->audit('save_created', $save, ['version' => $version]);
            } else {
                // 版本检查
                if ($version <= $save->version) {
                    throw new GameDataException(GameDataErrorCode::InvalidVersion);
                }

                $save->update([
                    'data' => $data,
                    'version' => $version,
                    'client_version' => $version,
                    'synced_at' => now(),
                    'checksum' => $checksum,
                ]);

                // 记录历史
                $this->recordHistory($save->id, $data, $version, SaveAction::Update);

                $this->audit('save_updated', $save, ['version' => $version]);
            }

            return $save->fresh();
        });
    }

    /**
     * 删除存档
     */
    public function deleteSave(int $userId, int $gameId): bool
    {
        $save = $this->getSave($userId, $gameId);

        if ($save === null) {
            throw new GameDataException(GameDataErrorCode::SaveNotFound);
        }

        return DB::transaction(function () use ($save) {
            // 记录删除历史
            $this->recordHistory($save->id, $save->data, $save->version, SaveAction::Delete);

            $this->audit('save_deleted', $save, ['version' => $save->version]);

            $save->delete();
            return true;
        });
    }

    /**
     * 获取存档历史
     *
     * @return \Illuminate\Database\Eloquent\Collection<int, GameSaveHistory>
     */
    public function getHistory(int $userId, int $gameId, int $limit = 20)
    {
        $save = $this->getSave($userId, $gameId);

        if ($save === null) {
            throw new GameDataException(GameDataErrorCode::SaveNotFound);
        }

        return GameSaveHistory::where('save_id', $save->id)
            ->orderByDesc('created_at')
            ->limit($limit)
            ->get();
    }

    /**
     * 记录存档历史
     */
    private function recordHistory(int $saveId, array $data, int $version, SaveAction $action): GameSaveHistory
    {
        return GameSaveHistory::create([
            'save_id' => $saveId,
            'data' => $data,
            'version' => $version,
            'action' => $action,
        ]);
    }

    /**
     * 检查存档大小
     */
    private function assertSaveSize(array $data): void
    {
        $maxSizeKb = config('kylin-game-data.max_save_size', 512);
        $jsonSize = strlen(json_encode($data, JSON_UNESCAPED_UNICODE));

        if ($jsonSize > $maxSizeKb * 1024) {
            throw new GameDataException(GameDataErrorCode::SaveTooLarge);
        }
    }

    /**
     * 记录存档审计日志
     *
     * 依据《网络安全法》和《个人信息保护法》，
     * 游戏存档创建、更新、删除等操作需记录审计日志，便于数据追溯。
     */
    private function audit(string $event, GameSave $save, array $extra = []): void
    {
        if (! class_exists(\Kylin\AuditLog\Models\AuditLog::class)) {
            return;
        }

        try {
            \Kylin\AuditLog\Models\AuditLog::create([
                'event' => $event,
                'model_type' => GameSave::class,
                'model_id' => $save->id,
                'new_values' => array_merge([
                    'user_id' => $save->user_id,
                    'game_id' => $save->game_id,
                ], $extra),
                'old_values' => null,
                'user_id' => auth()->id() ?? $save->user_id,
                'ip' => request()?->ip(),
                'url' => request()?->fullUrl(),
                'tag' => 'game-data',
            ]);
        } catch (\Throwable $e) {
            logger()->warning('游戏存档审计日志写入失败', [
                'error' => $e->getMessage(),
                'save_id' => $save->id,
                'event' => $event,
            ]);
        }
    }
}
