using GerbilManagerWebAPI.Models;
using Microsoft.EntityFrameworkCore;

namespace GerbilManagerWebAPI.Genetics
{
    /// <summary>
    /// Loads the pedigree out of the database into in-memory lookups (the home dataset
    /// is small) and delegates the actual maths to <see cref="PedigreeCalculator"/>.
    ///
    /// Parent links live in EF shadow foreign keys: a gerbil's birth litter is
    /// <c>Gerbils.LitterId</c>; that litter's parents are <c>Litters.FatherId</c> and
    /// <c>Litters.MotherId</c>.
    /// </summary>
    public sealed class InbreedingService
    {
        private readonly ApplicationContext _db;

        public InbreedingService(ApplicationContext db) => _db = db;

        /// <summary>F for an existing gerbil, or null if no such gerbil exists.</summary>
        public InbreedingResult? ForGerbil(Guid id)
        {
            var calculator = BuildCalculator();
            return calculator.Contains(id) ? calculator.ForIndividual(id) : null;
        }

        /// <summary>F for the hypothetical offspring of the given sire and dam.</summary>
        public InbreedingResult ForPairing(Guid? fatherId, Guid? motherId)
        {
            return BuildCalculator().ForOffspringOf(fatherId, motherId);
        }

        private PedigreeCalculator BuildCalculator()
        {
            var litters = _db.Litters
                .Select(l => new { l.Id, l.FatherId, l.MotherId })
                .ToDictionary(l => l.Id, l => (l.FatherId, l.MotherId));

            var gerbils = _db.Gerbils
                .Select(g => new { g.Id, g.Name, g.LitterId })
                .ToList();

            var parents = new Dictionary<Guid, (Guid? Father, Guid? Mother)>(gerbils.Count);
            var names = new Dictionary<Guid, string>(gerbils.Count);

            foreach (var g in gerbils)
            {
                names[g.Id] = g.Name;

                Guid? father = null;
                Guid? mother = null;
                if (g.LitterId is Guid litterId && litters.TryGetValue(litterId, out var litterParents))
                {
                    father = litterParents.FatherId;
                    mother = litterParents.MotherId;
                }

                parents[g.Id] = (father, mother);
            }

            return new PedigreeCalculator(parents, names);
        }
    }
}
