using System.Text.Json; using System.Text.Json.Serialization; namespace GerbilManagerWebAPI.Import.Rpro3 { /// /// Manuelle Entscheidungen der Züchterin zu mehrdeutigen RPRO3-Namensdubletten /// (aus den „Fehler melden"-Tickets, Kontext „rpro3-import"). Der automatische /// ist konservativ und legt nur bei positiver Evidenz zusammen; /// diese Overrides erlauben der Züchterin, gezielt zu korrigieren: /// /// • Same — Gruppen von RPRO3-Nummern (rids), die DASSELBE Tier sind /// (zwingt ein Merge über Varianten hinweg; ein rid je Variante genügt, /// der ganze Cluster wird mitgezogen). /// • Different— Gruppen, die VERSCHIEDENE Tiere sind (verhindert ein automatisches /// Zusammenlegen über Gruppengrenzen). /// • Fields — Feld-Korrekturen am resultierenden Tier (Farbe/Geburtsdatum/Herkunft/ /// Bestands-Flag/Zusatznotiz), adressiert über IRGENDEINE rid des Clusters. /// /// Datenquelle: Import/Rpro3/rpro3-decisions.json (siehe ). /// Stabiler Schlüssel ist die RPRO3-Nummer (rid), nicht der Variantenbuchstabe — Buchstaben /// verschieben sich, sobald sich die Clusterbildung ändert. /// public sealed class Rpro3Decisions { public List Decisions { get; set; } = new(); public static Rpro3Decisions Empty { get; } = new(); /// Lädt die Entscheidungen aus JSON; fehlt die Datei oder ist sie leer/kaputt, /// kommt eine leere Menge zurück (Import läuft dann mit reinem Auto-Dedup). public static Rpro3Decisions Load(string? path) { if (string.IsNullOrWhiteSpace(path) || !File.Exists(path)) return Empty; try { var json = File.ReadAllText(path); var d = JsonSerializer.Deserialize(json, JsonOpts); return d ?? Empty; } catch (JsonException) { return Empty; } } private static readonly JsonSerializerOptions JsonOpts = new(JsonSerializerDefaults.Web) { ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true, }; /// Alle „Same"-rid-Gruppen (für Force-Merge). public IEnumerable> SameGroups() { foreach (var d in Decisions) foreach (var g in d.Same) if (g.Count >= 2) yield return g; } /// rid → numerische Split-Gruppe je Entscheidung. Zwei rids mit unterschiedlicher /// Split-Gruppe (gleiche Entscheidung) dürfen NICHT automatisch zusammengelegt werden. public Dictionary BuildSplitGroups() { var map = new Dictionary(StringComparer.Ordinal); int gid = 0; foreach (var d in Decisions) { if (d.Different.Count < 2) continue; foreach (var grp in d.Different) { gid++; foreach (var rid in grp) map[rid] = gid; } } return map; } /// rid → Feld-Override (jede rid eines Clusters zeigt auf denselben Override). public Dictionary BuildFieldIndex() { var map = new Dictionary(StringComparer.Ordinal); foreach (var d in Decisions) foreach (var (rid, ov) in d.Fields) map[rid] = ov; return map; } } public sealed class Rpro3Decision { /// Anzeigename (nur zur Lesbarkeit der JSON; Logik nutzt rids). public string? Name { get; set; } /// Ticket-Id (Rückverfolgbarkeit). public string? Ticket { get; set; } public List> Same { get; set; } = new(); public List> Different { get; set; } = new(); public Dictionary Fields { get; set; } = new(); } public sealed class Rpro3FieldOverride { public string? Color { get; set; } public string? Dob { get; set; } // ISO yyyy-MM-dd public string? Origin { get; set; } public bool? Resident { get; set; } /// Zusatz, der an die Notizen des Tiers angehängt wird (z. B. abweichendes DOB). public string? Note { get; set; } } }