using PlotLine.Models; namespace PlotLine.Services; public sealed record CoreImportExistingCharacterName(int CharacterID, string Name); public sealed class CoreImportCharacterCandidateResolutionResult { public IReadOnlyList Candidates { get; init; } = []; public int AutoResolvedCount { get; init; } } public static class CoreImportCharacterCandidateResolver { public static CoreImportCharacterCandidateResolutionResult ResolveExactKnownMatches( IReadOnlyList candidates, IReadOnlyList existingNames) { var index = existingNames .Select(item => new { Key = CoreImportBasicLocationDetector.StableKey(item.Name), item.CharacterID }) .Where(item => !string.IsNullOrWhiteSpace(item.Key)) .GroupBy(item => item.Key, StringComparer.Ordinal) .ToDictionary( group => group.Key, group => group.Select(item => item.CharacterID).Distinct().ToList(), StringComparer.Ordinal); var resolved = 0; var output = candidates.Select(candidate => { var key = CoreImportBasicLocationDetector.StableKey(candidate.Name); if (!string.IsNullOrWhiteSpace(key) && index.TryGetValue(key, out var matches) && matches.Count == 1) { resolved++; return new ManuscriptScanCharacterCandidatePreview { TemporaryCharacterKey = candidate.TemporaryCharacterKey, Name = candidate.Name, MentionCount = candidate.MentionCount, QualityScore = candidate.QualityScore, Category = candidate.Category, Reason = candidate.Reason, ExistingCharacterID = matches[0], IsExistingCharacterMatch = true, IsAutoResolvedExistingCharacter = true, SuggestedImportance = candidate.SuggestedImportance, EvidenceScenes = candidate.EvidenceScenes }; } return candidate; }).ToList(); return new CoreImportCharacterCandidateResolutionResult { Candidates = output, AutoResolvedCount = resolved }; } }