using System.Text.Json;
using System.Text.Json.Serialization;

namespace GerbilManagerWebAPI.Import.Rpro3
{
    /// <summary>
    /// Manuelle Entscheidungen der Züchterin zu mehrdeutigen RPRO3-Namensdubletten
    /// (aus den „Fehler melden"-Tickets, Kontext „rpro3-import"). Der automatische
    /// <see cref="Rpro3Dedup"/> ist konservativ und legt nur bei positiver Evidenz zusammen;
    /// diese Overrides erlauben der Züchterin, gezielt zu korrigieren:
    ///
    ///   • <c>Same</c>     — 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).
    ///   • <c>Different</c>— Gruppen, die VERSCHIEDENE Tiere sind (verhindert ein automatisches
    ///                       Zusammenlegen über Gruppengrenzen).
    ///   • <c>Fields</c>   — Feld-Korrekturen am resultierenden Tier (Farbe/Geburtsdatum/Herkunft/
    ///                       Bestands-Flag/Zusatznotiz), adressiert über IRGENDEINE rid des Clusters.
    ///
    /// Datenquelle: <c>Import/Rpro3/rpro3-decisions.json</c> (siehe <see cref="Load"/>).
    /// Stabiler Schlüssel ist die RPRO3-Nummer (rid), nicht der Variantenbuchstabe — Buchstaben
    /// verschieben sich, sobald sich die Clusterbildung ändert.
    /// </summary>
    public sealed class Rpro3Decisions
    {
        public List<Rpro3Decision> Decisions { get; set; } = new();

        public static Rpro3Decisions Empty { get; } = new();

        /// <summary>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).</summary>
        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<Rpro3Decisions>(json, JsonOpts);
                return d ?? Empty;
            }
            catch (JsonException)
            {
                return Empty;
            }
        }

        private static readonly JsonSerializerOptions JsonOpts = new(JsonSerializerDefaults.Web)
        {
            ReadCommentHandling = JsonCommentHandling.Skip,
            AllowTrailingCommas = true,
        };

        /// <summary>Alle „Same"-rid-Gruppen (für Force-Merge).</summary>
        public IEnumerable<IReadOnlyList<string>> SameGroups()
        {
            foreach (var d in Decisions)
                foreach (var g in d.Same)
                    if (g.Count >= 2) yield return g;
        }

        /// <summary>rid → numerische Split-Gruppe je Entscheidung. Zwei rids mit unterschiedlicher
        /// Split-Gruppe (gleiche Entscheidung) dürfen NICHT automatisch zusammengelegt werden.</summary>
        public Dictionary<string, int> BuildSplitGroups()
        {
            var map = new Dictionary<string, int>(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;
        }

        /// <summary>rid → Feld-Override (jede rid eines Clusters zeigt auf denselben Override).</summary>
        public Dictionary<string, Rpro3FieldOverride> BuildFieldIndex()
        {
            var map = new Dictionary<string, Rpro3FieldOverride>(StringComparer.Ordinal);
            foreach (var d in Decisions)
                foreach (var (rid, ov) in d.Fields)
                    map[rid] = ov;
            return map;
        }
    }

    public sealed class Rpro3Decision
    {
        /// <summary>Anzeigename (nur zur Lesbarkeit der JSON; Logik nutzt rids).</summary>
        public string? Name { get; set; }
        /// <summary>Ticket-Id (Rückverfolgbarkeit).</summary>
        public string? Ticket { get; set; }
        public List<List<string>> Same { get; set; } = new();
        public List<List<string>> Different { get; set; } = new();
        public Dictionary<string, Rpro3FieldOverride> 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; }
        /// <summary>Roher Gencode (Fcode, z. B. „aa Cc[h] DD ee Gg P- spsp"). Überschreibt den
        /// automatisch aus den Cluster-Mitgliedern gewählten Gencode — nötig, wenn sich die
        /// Varianten im Gencode unterscheiden (z. B. C-Locus c[h] vs. c[chm]) und die Züchterin
        /// den korrekten Genotyp vorgibt.</summary>
        public string? Genotype { get; set; }
        public bool? Resident { get; set; }
        /// <summary>Zusatz, der an die Notizen des Tiers angehängt wird (z. B. abweichendes DOB).</summary>
        public string? Note { get; set; }
    }
}
