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 MemberDeliveryPerVarietyData : DataTable<MemberDeliveryPerVariantRow> {

        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),
            ("SortIds", "Sorte", null, 12),
            ("AttrIds", "Attribut", null, 16),
            ("Weights", "Geliefert", "kg", 22),
            ("Areas", "Fläche", "m²", 22),
            ("Yields", "Ertrag", "kg/ha", 22),
        ];

        public MemberDeliveryPerVarietyData(IEnumerable<MemberDeliveryPerVariantRow> rows, int year) :
            base($"Liefermengen", $"Liefermengen pro Mitglied, Sorte und Attribut {year}", rows, FieldNames) {
        }

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

        private static async Task<IEnumerable<MemberDeliveryPerVariantRowSingle>> FromDbSet(DbSet<MemberDeliveryPerVariantRowSingle> 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,
                           v.bucket, v.weight, v.area
                    FROM (
                            SELECT c.year AS year,
                                   c.mgnr AS mgnr,
                                   c.bucket AS bucket,
                                   COALESCE(d.weight, 0) AS weight,
                                   COALESCE(c.area, 0) AS area
                            FROM v_area_commitment_bucket_strict c
                                LEFT JOIN v_delivery_bucket_strict d ON (d.year, d.mgnr, d.bucket) = (c.year, c.mgnr, c.bucket)
                            WHERE c.year = {year}
                            UNION
                            SELECT d.year,
                                   d.mgnr,
                                   d.bucket,
                                   COALESCE(d.weight, 0),
                                   COALESCE(c.area, 0)
                            FROM v_delivery_bucket_strict d
                                LEFT JOIN v_area_commitment_bucket_strict c ON (c.year, c.mgnr, c.bucket) = (d.year, d.mgnr, d.bucket)
                            WHERE d.year = {year}
                        ) v
                        LEFT JOIN member m ON m.mgnr = v.mgnr
                        LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
                        LEFT JOIN AT_ort o ON o.okz = p.okz
                    ORDER BY m.mgnr, v.bucket
                    """).ToListAsync();
        }
    }

    public class MemberDeliveryPerVariantRow {
        public int MgNr;
        public string Name1;
        public string? Name2;
        public string Address;
        public int Plz;
        public string Locality;
        public string[] SortIds;
        public string[] AttrIds;
        public int[] Areas;
        public int[] Weights;
        public int?[] Yields => Weights.Zip(Areas).Select(i => i.Second > 0 ? (int?)i.First * 10_000 / i.Second : null).ToArray();

        public MemberDeliveryPerVariantRow(IEnumerable<MemberDeliveryPerVariantRowSingle> 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];
            SortIds = rows.Select(r => r.VtrgId[..2]).ToArray();
            AttrIds = rows.Select(r => r.VtrgId[2..]).ToArray();
            Areas = rows.Select(r => r.Area).ToArray();
            Weights = rows.Select(r => r.Weight).ToArray();
        }
    }

    [Keyless]
    public class MemberDeliveryPerVariantRowSingle {
        [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("weight")]
        public int Weight { get; set; }
    }
}