<?php

namespace Kylin\GameData\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
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;

class GameDataSyncService
{
    /**
     * 客户端同步到服务端
     *
     * 处理流程：
     * 1. 检查同步频率
     * 2. 检查版本冲突
     * 3. 冲突时按策略解决
     * 4. 保存数据并记录历史
     */
    public function syncFromClient(int $userId, int $gameId, array $data, int $clientVersion): GameSave
    {
        Log::info('[GameDataSync] 开始同步', [
            'user_id' => $userId,
            'game_id' => $gameId,
            'client_version' => $clientVersion,
            'client_stamina' => $data['stamina'] ?? 'N/A',
        ]);

        // 检查同步频率
        $this->assertSyncInterval($userId, $gameId);

        $save = GameSave::where('user_id', $userId)
            ->where('game_id', $gameId)
            ->first();

        Log::info('[GameDataSync] 查询存档', [
            'save_exists' => $save !== null,
            'server_version' => $save?->version,
            'server_stamina' => $save?->data['stamina'] ?? 'N/A',
        ]);

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

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

            $this->recordHistory($save->id, $data, 1, SaveAction::Create);

            return $save->fresh();
        }

        // 版本冲突检测：仅当客户端版本落后时才视为冲突（== 表示无并发修改，正常更新）
        if ($clientVersion < $save->version) {
            $resolvedData = $this->resolveConflict($save->data, $data);
            $newVersion = $save->version + 1;

            $save->update([
                'data' => $resolvedData,
                'version' => $newVersion,
                'client_version' => $clientVersion,
                'synced_at' => now(),
                'checksum' => $this->calculateChecksum($resolvedData),
            ]);

            // 记录冲突解决历史
            $this->recordHistory($save->id, $resolvedData, $newVersion, SaveAction::ConflictResolve);

            $this->audit('sync_conflict_resolved', $save, [
                'client_version' => $clientVersion,
                'server_version' => $newVersion,
            ]);

            return $save->fresh();
        }

        // 正常更新
        $newVersion = $save->version + 1;
        Log::info('[GameDataSync] 正常更新', [
            'saving_stamina' => $data['stamina'] ?? 'N/A',
            'new_version' => $newVersion,
        ]);
        $save->update([
            'data' => $data,
            'version' => $newVersion,
            'client_version' => $clientVersion,
            'synced_at' => now(),
            'checksum' => $checksum,
        ]);

        $this->recordHistory($save->id, $data, $newVersion, SaveAction::Update);

        $this->audit('sync_from_client', $save, [
            'client_version' => $clientVersion,
            'new_version' => $newVersion,
        ]);

        $fresh = $save->fresh();
        Log::info('[GameDataSync] 返回结果', [
            'return_version' => $fresh?->version,
            'return_stamina' => $fresh?->data['stamina'] ?? 'N/A',
        ]);
        return $fresh;
    }

    /**
     * 冲突解决（客户端优先，因为游戏数据由客户端生成）
     *
     * 策略：
     * - client_wins：采用客户端数据（默认，游戏场景客户端为数据源头）
     * - server_wins：保留服务端数据
     * - manual：抛出冲突异常，等待手动解决
     */
    public function resolveConflict(array $serverData, array $clientData): array
    {
        $strategy = config('kylin-game-data.conflict_strategy', 'client_wins');

        return match ($strategy) {
            'client_wins' => $clientData,
            'server_wins' => $serverData,
            'manual' => throw new GameDataException(GameDataErrorCode::SaveConflict),
            default => $clientData,
        };
    }

    /**
     * 计算数据校验和
     */
    public function calculateChecksum(array $data): string
    {
        $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        return hash('sha256', $json);
    }

    /**
     * 检查同步频率
     */
    private function assertSyncInterval(int $userId, int $gameId): void
    {
        $interval = config('kylin-game-data.sync_interval', 5);
        $key = "kylin:game-data:sync:{$userId}:{$gameId}";

        if (Cache::has($key)) {
            throw new GameDataException(GameDataErrorCode::SyncTooFrequent);
        }

        Cache::put($key, true, $interval);
    }

    /**
     * 记录存档历史
     */
    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 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-sync',
            ]);
        } catch (\Throwable $e) {
            logger()->warning('游戏同步审计日志写入失败', [
                'error' => $e->getMessage(),
                'save_id' => $save->id,
                'event' => $event,
            ]);
        }
    }
}
