using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
using System.Linq;
using System.Threading.Tasks;

namespace Elwig.Models.Dtos {
    public class PaymentVariantSummaryData : DataTable<PaymentVariantSummaryData.PaymentRow> {

        private static readonly (string, string, string?, int?)[] FieldNames = [
            ("Type", "Typ", null, 10),
            ("Variety", "Sorte", null, 40),
            ("Attribute", "Attribut", null, 20),
            ("Cultivation", "Bewirt.", null, 20),
            ("QualityLevel", "Qualitätsstufe", null, 30),
            ("Oe", "Gradation", "°Oe", 20),
            ("Ungeb", "ungebunden", "kg|€/kg|€/kg", 60),
            ("LowGeb", "attributlos gebunden", "kg|€/kg|€/kg", 60),
            ("Geb", "gebunden", "kg|€/kg|€/kg", 60),
            ("Amount", "Gesamt", "€", 25),
        ];

        public record struct PaymentRow(
            string Type,
            string Variety,
            string? Attribute,
            string? Cultivation,
            string QualityLevel,
            double Oe,
            (int Weight, decimal? MinPrice, decimal? MaxPrice) Ungeb,
            (int Weight, decimal? MinPrice, decimal? MaxPrice) LowGeb,
            (int Weight, decimal? MinPrice, decimal? MaxPrice) Geb,
            decimal Amount
        );

        public PaymentVariantSummaryData(PaymentVar v, IEnumerable<PaymentRow> rows) :
            base($"{PaymentVariantSummary.Name} {v.Year}", $"{PaymentVariantSummary.Name} Lese {v.Year}", v.Name, rows, FieldNames) {
        }

        public static async Task<PaymentVariantSummaryData> ForPaymentVariant(PaymentVar v, DbSet<PaymentVariantSummaryRow> table) {
            return new(v, (await FromDbSet(table, v.Year, v.AvNr))
                .Select(r => new PaymentRow(r.Type, r.Variety, r.Attribute, r.Cultivation, r.QualityLevel, r.Oe,
                    (r.WeightUngeb, r.MinPriceUngeb != null ? Utils.DecFromDb(r.MinPriceUngeb.Value, v.Season.Precision) : null, r.MaxPriceUngeb != null ? Utils.DecFromDb(r.MaxPriceUngeb.Value, v.Season.Precision) : null),
                    (r.WeightLowGeb, r.MinPriceLowGeb != null ? Utils.DecFromDb(r.MinPriceLowGeb.Value, v.Season.Precision) : null, r.MaxPriceLowGeb != null ? Utils.DecFromDb(r.MaxPriceLowGeb.Value, v.Season.Precision) : null),
                    (r.WeightGeb, r.MinPriceGeb != null ? Utils.DecFromDb(r.MinPriceGeb.Value, v.Season.Precision) : null, r.MaxPriceGeb != null ? Utils.DecFromDb(r.MaxPriceGeb.Value, v.Season.Precision) : null),
                    Utils.DecFromDb(r.Amount, v.Season.Precision)))
                .ToArray());
        }

        private static async Task<IEnumerable<PaymentVariantSummaryRow>> FromDbSet(DbSet<PaymentVariantSummaryRow> table, int year, int avnr) {
            return await table.FromSql($"""
                SELECT v.type AS type,
                       v.name AS variety,
                       a.name AS attribute,
                       c.name AS cultivation,
                       q.name AS quality_level,
                       ROUND(kmw * (4.54 + 0.022 * kmw)) AS oe,
                       SUM(IIF(w.discr = '_', w.value, 0)) AS weight_ungeb,
                       MIN(IIF(w.discr = '_', b.price, NULL)) AS min_price_ungeb,
                       MAX(IIF(w.discr = '_', b.price, NULL)) AS max_price_ungeb,
                       SUM(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), w.value, 0)) AS weight_lowgeb,
                       MIN(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), b.price, NULL)) AS min_price_lowgeb,
                       MAX(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), b.price, NULL)) AS max_price_lowgeb,
                       SUM(IIF(w.discr = COALESCE(p.attrid, ''), w.value, 0)) AS weight_geb,
                       MIN(IIF(w.discr = COALESCE(p.attrid, ''), b.price, NULL)) AS min_price_geb,
                       MAX(IIF(w.discr = COALESCE(p.attrid, ''), b.price, NULL)) AS max_price_geb,
                       SUM(b.amount) AS amount
                FROM payment_delivery_part_bucket b
                    LEFT JOIN delivery_part_bucket w ON (w.year, w.did, w.dpnr, w.bktnr) = (b.year, b.did, b.dpnr, b.bktnr)
                    LEFT JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (b.year, b.did, b.dpnr)
                    LEFT JOIN wine_variety v ON v.sortid = p.sortid
                    LEFT JOIN wine_attribute a ON a.attrid = p.attrid
                    LEFT JOIN wine_cultivation c ON c.cultid = p.cultid
                    LEFT JOIN wine_quality_level q ON q.qualid = p.qualid
                WHERE p.year = {year} AND b.avnr = {avnr}
                GROUP BY variety, attribute, cultivation, q.min_kmw, oe
                ORDER BY variety, attribute, cultivation, q.min_kmw, oe
                """).ToListAsync();
        }
    }

    [Keyless]
    public class PaymentVariantSummaryRow {
        [Column("type")]
        public required string Type { get; set; }
        [Column("variety")]
        public required string Variety { get; set; }
        [Column("attribute")]
        public string? Attribute { get; set; }
        [Column("cultivation")]
        public string? Cultivation { get; set; }
        [Column("quality_level")]
        public required string QualityLevel { get; set; }
        [Column("oe")]
        public double Oe { get; set; }
        [Column("weight_ungeb")]
        public int WeightUngeb { get; set; }
        [Column("min_price_ungeb")]
        public long? MinPriceUngeb { get; set; }
        [Column("max_price_ungeb")]
        public long? MaxPriceUngeb { get; set; }
        [Column("weight_lowgeb")]
        public int WeightLowGeb { get; set; }
        [Column("min_price_lowgeb")]
        public long? MinPriceLowGeb { get; set; }
        [Column("max_price_lowgeb")]
        public long? MaxPriceLowGeb { get; set; }
        [Column("weight_geb")]
        public int WeightGeb { get; set; }
        [Column("min_price_geb")]
        public long? MinPriceGeb { get; set; }
        [Column("max_price_geb")]
        public long? MaxPriceGeb { get; set; }
        [Column("amount")]
        public long Amount { get; set; }
    }
}
