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

namespace Elwig.Models.Dtos {
    class MemberAreaComsData : DataTable<MemberAreaComsRow> {

        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),
            ("Areas", "Fläche", "m²", 22),
            ("DeliveryObligations", "Lieferpflicht", "kg", 22),
            ("DeliveryRights", "Lieferrecht", "kg", 22),
        ];

        public MemberAreaComsData(IEnumerable<MemberAreaComsRow> rows, int year) :
            base($"Flächenbindungen", $"Flächenbindungen pro Mitglied {year}", rows, FieldNames) {
        }

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

        private static async Task<IEnumerable<MemberAreaComsRowSingle>> FromDbSet(DbSet<MemberAreaComsRowSingle> 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, c.min_kg, c.max_kg
                    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
                    WHERE c.year = {year}
                    ORDER BY m.mgnr, c.bucket
                    """).ToListAsync();
        }
    }

    public class MemberAreaComsRow {
        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[] DeliveryObligations;
        public int[] DeliveryRights;

        public MemberAreaComsRow(IEnumerable<MemberAreaComsRowSingle> 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();
            DeliveryObligations = rows.Select(r => r.MinKg).ToArray();
            DeliveryRights = rows.Select(r => r.MaxKg).ToArray();
        }
    }

    [Keyless]
    public class MemberAreaComsRowSingle {
        [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 MinKg { get; set; }
        [Column("max_kg")]
        public int MaxKg { get; set; }
    }
}
