using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Elwig.Models.Dtos {
    public class DeliveryConfirmationData : DataTable<DeliveryConfirmationRow> {

        private static readonly (string, string, string?, int)[] FieldNames = new[] {
            ("LsNr", "LsNr.", null, 26),
            ("DPNr", "Pos.", null, 8),
            ("Variant", "Sorte", null, 40),
            ("Attribute", "Attribut", null, 20),
            ("Modifiers", "Zu-/Abschläge", null, 30),
            ("QualityLevel", "Qualitätsstufe", null, 25),
            ("Gradation", "Gradation", "°Oe|°KMW", 32),
            ("Buckets", "Flächenbindung", "|kg", 36),
            ("Weight", "Gewicht", "kg", 16),
        };

        private readonly int MgNr;

        private DeliveryConfirmationData(IEnumerable<DeliveryConfirmationRow> rows, int year, Member m) :
            base($"Anlieferungsbestätigung", $"Anlieferungsbestätigung {year} – {m.AdministrativeName}", rows, FieldNames) {
            MgNr = m.MgNr;
        }

        public static async Task<IDictionary<int, DeliveryConfirmationData>> ForSeason(DbSet<DeliveryPart> table, int year) {
            return (await FromDbSet(table, year))
                .GroupBy(
                    p => p.Delivery.Member,
                    p => new DeliveryConfirmationRow(p),
                    (k, g) => new DeliveryConfirmationData(g, year, k)
                ).ToDictionary(d => d.MgNr, d => d);
        }

        public static async Task<DeliveryConfirmationData> ForMember(DbSet<DeliveryPart> table, int year, Member m) {
            return new DeliveryConfirmationData((await FromDbSet(table, year, m.MgNr)).Select(p => new DeliveryConfirmationRow(p)), year, m);
        }

        private static async Task<IEnumerable<DeliveryPart>> FromDbSet(DbSet<DeliveryPart> table, int? year = null, int? mgnr = null) {
            var y = year?.ToString() ?? "NULL";
            var m = mgnr?.ToString() ?? "NULL";
            IQueryable<DeliveryPart> q = table;
            if (year != null) q = q.Where(p => p.Year == year);
            if (mgnr != null) q = q.Where(p => p.Delivery.MgNr == mgnr);
            await q
                 .Include(p => p.Delivery)
                 .Include(p => p.Variant)
                 .Include(p => p.Attribute)
                 .Include(p => p.Quality)
                 .Include(p => p.Buckets)
                 .Include(p => p.PartModifiers)
                 .ThenInclude(m => m.Modifier)
                 .LoadAsync();
            return await table.FromSqlRaw($"""
                    SELECT p.*
                    FROM v_delivery v
                        JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (v.year, v.did, v.dpnr)
                    WHERE (p.year = {y} OR {y} IS NULL) AND (v.mgnr = {m} OR {m} IS NULL)
                    ORDER BY p.year, v.mgnr, v.sortid, v.abgewertet ASC, v.attribute_prio DESC, COALESCE(v.attrid, '~'), v.kmw DESC, v.lsnr, v.dpnr
                    """).ToListAsync();
        }
    }

    public class DeliveryConfirmationRow {
        public string LsNr;
        public int DPNr;
        public string Variant;
        public string? Attribute;
        public string QualityLevel;
        public (double Oe, double Kmw) Gradation;
        public string[] Modifiers;
        public int Weight;
        public (string Name, int Value)[] Buckets;

        public DeliveryConfirmationRow(DeliveryPart p) {
            var d = p.Delivery;
            LsNr = d.LsNr;
            DPNr = p.DPNr;
            Variant = p.Variant.Name;
            Attribute = p.Attribute?.Name;
            QualityLevel = p.Quality.Name;
            Gradation = (p.Oe, p.Kmw);
            Modifiers = p.Modifiers
                .Select(m => m.Name)
                .ToArray();
            Weight = p.Weight;
            Buckets = p.Buckets
                .Where(b => b.Value > 0)
                .OrderByDescending(b => b.BktNr)
                .Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {p.SortId}{b.Discr}", b.Value))
                .ToArray();
        }
    }
}