namespace Shared.Excel; public sealed class ImportMappingEngine { public IReadOnlyList Map( IReadOnlyList sourceColumns, ImportDefinition definition, IReadOnlyList? saved = null) { var savedBySource = (saved ?? []) .Where(x => x.TargetField is not null) .ToDictionary(x => Normalize(x.SourceColumn), StringComparer.OrdinalIgnoreCase); var result = new List(sourceColumns.Count); foreach (var source in sourceColumns) { var normalized = Normalize(source); if (savedBySource.TryGetValue(normalized, out var savedMapping) && definition.Fields.Any(x => x.Key.Equals(savedMapping.TargetField, StringComparison.OrdinalIgnoreCase))) { result.Add(savedMapping with { SourceColumn = source, Source = "saved", Confidence = 1m, Reason = "저장된 매핑" }); continue; } var exact = definition.Fields.FirstOrDefault(x => Normalize(x.Label) == normalized || Normalize(x.Key) == normalized); if (exact is not null) { result.Add(new(source, exact.Key, "exact", 1m, "필드명 일치")); continue; } var alias = definition.Fields.FirstOrDefault(x => x.Aliases.Any(a => Normalize(a) == normalized)); if (alias is not null) { result.Add(new(source, alias.Key, "alias", 1m, $"별칭 '{source}' 일치")); continue; } result.Add(new(source, null, "manual", null, null)); } return result; } private static string Normalize(string value) => new(value.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray()); }