using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;

namespace Elwig.Models.Dtos {
    public class AreaComUnderDeliveryData : DataTable<AreaComUnderDeliveryRow> {

        private static readonly (string, string, string?, int)[] FieldNames = [
            ("MgNr", "MgNr.", null, 12),
            ("Name1", "Name", null, 40),
            ("Name2", "Vorname", null, 40),
            ("Address", "Adresse", null, 60),
            ("Plz", "PLZ", null, 10),
            ("Locality", "Ort", null, 60),
            ("VtrgIds", "Vertrag", null, 14),
            ("Areas", "Fläche", "m²", 16),
            ("DeliveryObligations", "Lieferpflicht", "kg", 22),
            ("Weights", "Geliefert", "kg", 22),
            ("UnderDeliveries", "Unterliefert", "kg|%", 34),
        ];

        public AreaComUnderDeliveryData(IEnumerable<AreaComUnderDeliveryRow> rows, int year) :
            base($"Unterlieferungen FB", $"Unterlieferungen laut Flächenbindungen {year}", rows, FieldNames) {
        }

        public static async Task<AreaComUnderDeliveryData> ForSeason(DbSet<AreaComUnderDeliveryRowSingle> table, int year) {
            return new AreaComUnderDeliveryData(
                (await FromDbSet(table, year)).GroupBy(
                    r => r.MgNr,
                    (k, g) => new AreaComUnderDeliveryRow(g)
              ), year);
        }

        private static async Task<IEnumerable<AreaComUnderDeliveryRowSingle>> FromDbSet(DbSet<AreaComUnderDeliveryRowSingle> table, int year) {
            return await table.FromSql($"""
                    SELECT m.mgnr, m.name AS name_1,
                           COALESCE(m.prefix || ' ', '') || m.given_name ||
                           COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
                           p.plz, o.name AS ort, m.address,
                           c.bucket, c.area, u.min_kg, u.weight
                    FROM v_area_commitment_bucket_strict c
                        LEFT JOIN member m ON m.mgnr = c.mgnr
                        LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
                        LEFT JOIN AT_ort o ON o.okz = p.okz
                        JOIN v_under_delivery u ON (u.mgnr, u.bucket, u.year) = (m.mgnr, c.bucket, c.year)
                    WHERE c.year = {year}
                    ORDER BY m.mgnr, c.bucket
                    """).ToListAsync();
        }
    }

    public class AreaComUnderDeliveryRow {
        public int MgNr;
        public string Name1;
        public string? Name2;
        public string Address;
        public int Plz;
        public string Locality;
        public string[] VtrgIds;
        public int[] Areas;
        public int[] DeliveryObligations;
        public int[] Weights;
        public (int? Kg, double? Percent)[] UnderDeliveries => Weights.Zip(DeliveryObligations)
            .Select(v => v.First < v.Second ? ((int?, double?))(v.First - v.Second, v.First * 100.0 / v.Second - 100.0) : (null, null))
            .ToArray();

        public AreaComUnderDeliveryRow(IEnumerable<AreaComUnderDeliveryRowSingle> rows) {
            var f = rows.First();
            MgNr = f.MgNr;
            Name1 = f.Name1;
            Name2 = f.Name2;
            Address = f.Address;
            Plz = f.Plz;
            Locality = f.Locality.Split(",")[0];
            VtrgIds = rows.Select(r => r.VtrgId).ToArray();
            Areas = rows.Select(r => r.Area).ToArray();
            DeliveryObligations = rows.Select(r => r.DeliveryObligation).ToArray();
            Weights = rows.Select(r => r.Weight).ToArray();
        }
    }

    [Keyless]
    public class AreaComUnderDeliveryRowSingle {
        [Column("mgnr")]
        public int MgNr { get; set; }
        [Column("name_1")]
        public required string Name1 { get; set; }
        [Column("name_2")]
        public string? Name2 { get; set; }
        [Column("address")]
        public required string Address { get; set; }
        [Column("plz")]
        public int Plz { get; set; }
        [Column("ort")]
        public required string Locality { get; set; }
        [Column("bucket")]
        public required string VtrgId { get; set; }
        [Column("area")]
        public int Area { get; set; }
        [Column("min_kg")]
        public int DeliveryObligation { get; set; }
        [Column("weight")]
        public int Weight { get; set; }
    }
}