<?php

namespace Kylin\WordFilter\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Kylin\WordFilter\Enums\WordStatus;
use Kylin\WordFilter\Models\SensitiveWord;
use Kylin\WordFilter\Models\WordCategory;
use Kylin\WordFilter\Models\WordFilterVersion;

/**
 * 词库同步服务
 *
 * 负责词库版本管理、增量同步、版本发布等操作。
 */
class WordSyncService
{
    public function __construct(
        private readonly WordFilterService $filterService,
    ) {}

    /**
     * 获取当前词库版本信息
     *
     * @return array{version: string, total_words: int, category_stats: array, checksum: string, published_at: string|null}
     */
    public function getVersion(): array
    {
        $cacheKey = config('kylin-word-filter.version_cache_key');
        $cacheTtl = config('kylin-word-filter.cache_ttl', 3600);

        return Cache::remember($cacheKey, $cacheTtl, function (): array {
            $version = WordFilterVersion::latestPublished();

            if ($version === null) {
                return [
                    'version' => '0.0.0',
                    'total_words' => 0,
                    'category_stats' => [],
                    'checksum' => '',
                    'published_at' => null,
                ];
            }

            return [
                'version' => $version->version,
                'total_words' => $version->total_words,
                'category_stats' => $version->category_stats ?? [],
                'checksum' => $version->checksum,
                'published_at' => $version->published_at?->toIso8601String(),
            ];
        });
    }

    /**
     * 从指定版本增量同步词库
     *
     * @param  string  $fromVersion  起始版本号
     * @return array{from_version: string, to_version: string, added: int, removed: int, changed_categories: array, words: array{added: array<int, array{id: int, word: string, category: string, severity: string}>, removed: array<int, array{id: int, word: string, category: string, severity: string}>}}
     */
    public function syncFromVersion(string $fromVersion): array
    {
        $olderVersion = WordFilterVersion::findByVersion($fromVersion);
        $currentVersion = WordFilterVersion::latestPublished();

        if ($olderVersion === null) {
            // 旧版本不存在，返回全量数据
            return $this->fullSync($currentVersion);
        }

        if ($currentVersion === null || $olderVersion->version === $currentVersion->version) {
            return [
                'from_version' => $fromVersion,
                'to_version' => $fromVersion,
                'added' => 0,
                'removed' => 0,
                'changed_categories' => [],
                'words' => [
                    'added' => [],
                    'removed' => [],
                ],
            ];
        }

        $diff = $currentVersion->diffFrom($olderVersion);

        // 获取增量词库数据
        $addedWords = $this->getWordsBetweenVersions($olderVersion, $currentVersion, 'added');
        $removedWords = $this->getWordsBetweenVersions($olderVersion, $currentVersion, 'removed');

        return [
            'from_version' => $fromVersion,
            'to_version' => $currentVersion->version,
            'added' => $diff['added'],
            'removed' => $diff['removed'],
            'changed_categories' => $diff['changed_categories'],
            'words' => [
                'added' => $addedWords,
                'removed' => $removedWords,
            ],
        ];
    }

    /**
     * 发布新版本
     *
     * 基于当前词库状态创建新版本快照，计算校验和和分类统计。
     *
     * @param  string|null  $changeSummary  变更摘要
     * @return WordFilterVersion
     */
    public function publishVersion(?string $changeSummary = null): WordFilterVersion
    {
        $version = DB::transaction(function () use ($changeSummary): WordFilterVersion {
            // 计算分类统计
            $categoryStats = WordCategory::query()
                ->withCount(['words' => fn ($q) => $q->active()])
                ->get()
                ->mapWithKeys(fn (WordCategory $cat) => [
                    $cat->code => $cat->words_count,
                ])
                ->toArray();

            // 计算总词数
            $totalWords = SensitiveWord::active()->count();

            // 计算校验和
            $checksum = $this->calculateChecksum();

            // 生成版本号
            $latestVersion = WordFilterVersion::latestPublished();
            $newVersion = $this->incrementVersion($latestVersion?->version ?? '0.0.0');

            // 创建版本记录
            $version = WordFilterVersion::create([
                'version' => $newVersion,
                'total_words' => $totalWords,
                'category_stats' => $categoryStats,
                'checksum' => $checksum,
                'change_summary' => $changeSummary,
                'published_at' => now(),
            ]);

            // 清除版本缓存，触发 Trie 树重建
            $this->clearVersionCache();
            $this->filterService->rebuildTrie();

            return $version;
        });

        $this->audit('word_version_published', $version->id, [
            'version' => $version->version,
            'total_words' => $version->total_words,
            'checksum' => $version->checksum,
            'change_summary' => $changeSummary,
        ]);

        return $version;
    }

    /**
     * 计算词库校验和
     */
    private function calculateChecksum(): string
    {
        $words = SensitiveWord::active()
            ->orderBy('word')
            ->pluck('word')
            ->implode('|');

        return md5($words);
    }

    /**
     * 递增版本号（语义化版本）
     */
    private function incrementVersion(string $currentVersion): string
    {
        $parts = explode('.', $currentVersion);
        $patch = (int) ($parts[2] ?? 0);

        return sprintf('%d.%d.%d', (int) $parts[0], (int) $parts[1], $patch + 1);
    }

    /**
     * 全量同步
     */
    private function fullSync(?WordFilterVersion $currentVersion): array
    {
        $allWords = SensitiveWord::active()
            ->with('category')
            ->get()
            ->map(fn (SensitiveWord $word) => [
                'id' => $word->id,
                'word' => $word->word,
                'category' => $word->category?->code ?? 'unknown',
                'severity' => $word->severity->value,
            ])
            ->toArray();

        return [
            'from_version' => '0.0.0',
            'to_version' => $currentVersion?->version ?? '0.0.0',
            'added' => count($allWords),
            'removed' => 0,
            'changed_categories' => [],
            'words' => [
                'added' => $allWords,
                'removed' => [],
            ],
        ];
    }

    /**
     * 获取两个版本之间的增量词库
     *
     * @return array<int, array{id: int, word: string, category: string, severity: string}>
     */
    private function getWordsBetweenVersions(WordFilterVersion $older, WordFilterVersion $current, string $type): array
    {
        // 基于更新时间获取增量数据
        $query = SensitiveWord::with('category');

        if ($type === 'added') {
            $query->where('created_at', '>', $older->published_at)
                ->where('created_at', '<=', $current->published_at);
        } else {
            $query->onlyTrashed()
                ->where('deleted_at', '>', $older->published_at)
                ->where('deleted_at', '<=', $current->published_at);
        }

        return $query->get()
            ->map(fn (SensitiveWord $word) => [
                'id' => $word->id,
                'word' => $word->word,
                'category' => $word->category?->code ?? 'unknown',
                'severity' => $word->severity->value,
            ])
            ->toArray();
    }

    /**
     * 清除版本缓存
     */
    private function clearVersionCache(): void
    {
        Cache::forget(config('kylin-word-filter.version_cache_key'));
    }

    /**
     * 记录词库同步审计日志
     *
     * 依据《网络安全法》第四十七条和等保 2.0 (GB/T 22239) 8.1.4.6，
     * 词库版本发布、同步等管理操作需记录审计日志，便于内容审核追溯。
     */
    private function audit(string $event, int $modelId, array $extra = []): void
    {
        if (! class_exists(\Kylin\AuditLog\Models\AuditLog::class)) {
            return;
        }

        try {
            \Kylin\AuditLog\Models\AuditLog::create([
                'event' => $event,
                'model_type' => 'Kylin\WordFilter\Models\WordFilterVersion',
                'model_id' => $modelId,
                'new_values' => $extra,
                'old_values' => null,
                'user_id' => auth()->id(),
                'ip' => request()?->ip(),
                'url' => request()?->fullUrl(),
                'tag' => 'word-filter-sync',
            ]);
        } catch (\Throwable $e) {
            Log::warning('词库同步审计日志写入失败', [
                'error' => $e->getMessage(),
                'event' => $event,
                'model_id' => $modelId,
            ]);
        }
    }
}
