using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;

namespace Elwig.Helpers.Billing {
    public class PaymentBillingData : BillingData {

        protected readonly Dictionary<int, Curve> Curves;
        protected readonly Dictionary<string, Curve> PaymentData;
        protected readonly Dictionary<string, Curve> QualityData;
        protected readonly IEnumerable<string> AttributeVariants;

        public PaymentBillingData(JsonObject data, IEnumerable<string> attributeVariants) :
            base(data) {
            if (attributeVariants.Any(e => e.Any(c => c < 'A' || c > 'Z')))
                throw new ArgumentException("Invalid attributeVariants");
            AttributeVariants = attributeVariants;
            Curves = GetCurves();
            PaymentData = GetPaymentData();
            QualityData = GetQualityData();
        }

        public static PaymentBillingData FromJson(string json, IEnumerable<string> attributeVariants) {
            return new(ParseJson(json), attributeVariants);
        }

        private Dictionary<string, Curve> GetData(JsonNode data) {
            return GetSelection(data, AttributeVariants).ToDictionary(e => e.Key, e => LookupCurve(e.Value));
        }

        protected Dictionary<string, Curve> GetPaymentData() {
            return GetData(GetPaymentEntry());
        }

        protected Dictionary<string, Curve> GetQualityData() {
            Dictionary<string, Curve> dict = [];
            var q = GetQualityEntry();
            if (q == null) return dict;

            foreach (var (qualid, data) in q) {
                foreach (var (idx, d) in GetData(data ?? throw new InvalidOperationException())) {
                    dict[$"{qualid}/{idx}"] = d;
                }
            }

            return dict;
        }

        public decimal CalculatePrice(string sortid, string? attrid, string qualid, bool gebunden, double oe, double kmw) {
            var curve = GetQualityCurve(qualid, sortid, attrid) ?? GetCurve(sortid, attrid);
            var d = (gebunden ? curve.Gebunden : null) ?? curve.Normal;
            if (d.Count == 1) return d.First().Value;

            var r = curve.Mode == CurveMode.Oe ? oe : kmw;
            var lt = d.Keys.Where(v => v <= r);
            var gt = d.Keys.Where(v => v >= r);
            if (!lt.Any()) {
                return d[gt.Min()];
            } else if (!gt.Any()) {
                return d[lt.Max()];
            }

            var max = lt.Max();
            var min = gt.Min();
            if (max == min) return d[r];

            var p1 = ((decimal)r - (decimal)min) / ((decimal)max - (decimal)min);
            var p2 = 1 - p1;
            return d[min] * p2 + d[max] * p1;
        }

        private Curve LookupCurve(JsonValue val) {
            if (val.TryGetValue(out string? curve)) {
                var curveId = int.Parse(curve.Split(":")[1]);
                return Curves[curveId];
            } else if (val.TryGetValue(out decimal value)) {
                return new(CurveMode.Oe, new() { { 73, value } }, null);
            }
            throw new InvalidOperationException();
        }

        protected Curve GetCurve(string sortid, string? attrid) {
            return PaymentData[$"{sortid}{attrid ?? ""}"];
        }

        protected Curve? GetQualityCurve(string qualid, string sortid, string? attrid) {
            return QualityData.TryGetValue($"{qualid}/{sortid}{attrid ?? ""}", out var curve) ? curve : null;
        }
    }
}