using Elwig.Models.Entities;
using Microsoft.Data.Sqlite;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;

namespace Elwig.Helpers {
    public class ClientParameters {

        public enum Type { Matzen, Winzerkeller, Weinland, Baden };

        public bool IsMatzen => Client == Type.Matzen;
        public bool IsWinzerkeller => Client == Type.Winzerkeller;
        public bool IsWeinland => Client == Type.Weinland;
        public bool IsBaden => Client == Type.Baden;
        public bool IsWolkersdorf => IsWinzerkeller && App.ZwstId == "W";
        public bool IsHaugsdorf => IsWinzerkeller && App.ZwstId == "H";
        public bool IsSitzendorf => IsWinzerkeller && App.ZwstId == "S";
        public bool IsGrInzersdorf => IsWeinland;

        public string NameToken;
        public string NameShort;
        public string Name;
        public string? NameSuffix;
        public string NameType;
        public string NameTypeFull => NameType.Replace(".", "").Replace(" ", "").ToLower() switch {
            "reggenmbh" => "registrierte Genossenschaft mit beschränkter Haftung",
            _ => NameType,
        };
        public string NameFull => NameSuffix == null ? $"{Name} {NameType}" : $"{Name}, {NameSuffix}, {NameType}";
        public Type? Client;

        public PostalDest PostalDest {
            set {
                Plz = value.AtPlz!.Plz;
                Ort = value.AtPlz!.Ort.Name;
            }
        }
        public int Plz;
        public string Ort;
        public string Address;
        public string Sender1 => $"{NameShort} | {Address} | {Plz} {Ort}";
        public string Sender2;

        public string? Iban;
        public string? Bic;
        public string? UstIdNr;
        public string? LfbisNr;
        public string? OrganicAuthority;

        public string? PhoneNr;
        public string? FaxNr;
        public string? EmailAddress;
        public string? Website;

        public int ModeDeliveryNoteStats;
        public int ModeWineQualityStatistics;
        public int OrderingMemberList;

        public string? TextDeliveryNote;
        public string? TextDeliveryConfirmation;
        public string? TextCreditNote;
        public string? TextEmailSubject;
        public string? TextEmailBody;

        public bool MailIncludeNonDeliverers;
        public bool MailDoublePaged;
        public int MailSendPostal;
        public int MailSendEmail;
        public int MailOrdering;

        public int ExportEbicsVersion;
        public int ExportEbicsAddress;

        public (int? AllowanceKg, double? AllowanceBs, int? AllowanceKgPerBs, double? AllowancePercent, int? MinBs) AutoAdjustBs;

        public ClientParameters(AppDbContext ctx) : this(ctx.ClientParameters.ToDictionary(e => e.Param, e => e.Value)) { }

        public ClientParameters(Dictionary<string, string?> parameters) {
            try {
                NameToken = parameters["CLIENT_NAME_TOKEN"] ?? throw new KeyNotFoundException();
                NameShort = parameters["CLIENT_NAME_SHORT"] ?? throw new KeyNotFoundException();
                Name = parameters["CLIENT_NAME"] ?? throw new KeyNotFoundException();
                NameSuffix = parameters.GetValueOrDefault("CLIENT_NAME_SUFFIX");
                NameType = parameters["CLIENT_NAME_TYPE"] ?? throw new KeyNotFoundException();
                switch (Name) {
                    case "Winzergenossenschaft für Matzen und Umgebung":
                        Client = Type.Matzen; break;
                    case "Winzerkeller im Weinviertel":
                        Client = Type.Winzerkeller; break;
                    case "Winzergenossenschaft Weinland":
                        Client = Type.Weinland; break;
                    case "Winzergenossenschaft Baden - Bad Vöslau":
                        Client = Type.Baden; break;
                };

                Plz = int.Parse(parameters["CLIENT_PLZ"] ?? "");
                Ort = parameters["CLIENT_ORT"] ?? throw new KeyNotFoundException();
                Address = parameters["CLIENT_ADDRESS"] ?? throw new KeyNotFoundException();
                PhoneNr = parameters.GetValueOrDefault("CLIENT_PHONE");
                FaxNr = parameters.GetValueOrDefault("CLIENT_FAX");
                EmailAddress = parameters.GetValueOrDefault("CLIENT_EMAIL");
                Website = parameters.GetValueOrDefault("CLIENT_WEBSITE");
                LfbisNr = parameters.GetValueOrDefault("CLIENT_LFBISNR");
                UstIdNr = parameters.GetValueOrDefault("CLIENT_USTIDNR");
                Bic = parameters.GetValueOrDefault("CLIENT_BIC");
                Iban = parameters.GetValueOrDefault("CLIENT_IBAN");
                OrganicAuthority = parameters.GetValueOrDefault("CLIENT_ORGANIC_AUTHORITY");

                switch (parameters.GetValueOrDefault("MODE_DELIVERYNOTE_STATS", "SHORT")?.ToUpper()) {
                    case "NONE": ModeDeliveryNoteStats = 0; break;
                    case "GA_ONLY": ModeDeliveryNoteStats = 1; break;
                    case "SHORT": ModeDeliveryNoteStats = 2; break;
                    case "FULL": ModeDeliveryNoteStats = 3; break;
                }
                switch (parameters.GetValueOrDefault("MODE_WINEQUALITYSTATISTICS", "OE")?.ToUpper()) {
                    case "OE": ModeWineQualityStatistics = 0; break;
                    case "KMW/1": ModeWineQualityStatistics = 1; break;
                    case "KMW/2": ModeWineQualityStatistics = 2; break;
                    case "KMW/5": ModeWineQualityStatistics = 3; break;
                    case "KMW/10": ModeWineQualityStatistics = 4; break;
                }
                switch (parameters.GetValueOrDefault("ORDERING_MEMBERLIST", "MGNR")?.ToUpper()) {
                    case "MGNR": OrderingMemberList = 0; break;
                    case "NAME": OrderingMemberList = 1; break;
                    case "KG": OrderingMemberList = 2; break;
                }

                Sender2 = parameters.GetValueOrDefault("DOCUMENT_SENDER") ?? "";
                TextDeliveryNote = parameters.GetValueOrDefault("TEXT_DELIVERYNOTE");
                if (TextDeliveryNote == "") TextDeliveryNote = null;
                TextDeliveryConfirmation = parameters.GetValueOrDefault("TEXT_DELIVERYCONFIRMATION");
                if (TextDeliveryConfirmation == "") TextDeliveryConfirmation = null;
                TextCreditNote = parameters.GetValueOrDefault("TEXT_CREDITNOTE");
                if (TextCreditNote == "") TextCreditNote = null;
                TextEmailSubject = parameters.GetValueOrDefault("TEXT_EMAIL_SUBJECT");
                if (TextEmailSubject == "") TextEmailSubject = null;
                TextEmailBody = parameters.GetValueOrDefault("TEXT_EMAIL_BODY");
                if (TextEmailBody == "") TextEmailBody = null;

                MailIncludeNonDeliverers = (parameters.GetValueOrDefault("MAIL_INCLUDE_NON_DELIVERERS")?.ToUpper()) switch {
                    "1" or "TRUE" or "YES" or "JA" => true,
                    _ => false,
                };
                MailDoublePaged = (parameters.GetValueOrDefault("MAIL_DOUBLE_PAGED")?.ToUpper()) switch {
                    "1" or "TRUE" or "YES" or "JA" => true,
                    _ => false,
                };
                switch (parameters.GetValueOrDefault("MAIL_SEND_POSTAL", "WISH")?.ToUpper()) {
                    case "ALL": MailSendPostal = 3; break;
                    case "WISH": MailSendPostal = 2; break;
                    case "NO_EMAIL": MailSendPostal = 1; break;
                    case "NONE": MailSendPostal = 0; break;
                }
                switch (parameters.GetValueOrDefault("MAIL_SEND_EMAIL", "WISH")?.ToUpper()) {
                    case "ALL": MailSendEmail = 2; break;
                    case "WISH": MailSendEmail = 1; break;
                    case "NONE": MailSendEmail = 0; break;
                }
                switch (parameters.GetValueOrDefault("MAIL_ORDERING", "MGNR")?.ToUpper()) {
                    case "MGNR": MailOrdering = 0; break;
                    case "NAME": MailOrdering = 1; break;
                    case "PLZ": MailOrdering = 2; break;
                }

                ExportEbicsVersion = int.TryParse(parameters.GetValueOrDefault("EXPORT_EBICS_VERSION"), out var v) ? v : 9;
                switch (parameters.GetValueOrDefault("EXPORT_EBICS_ADDRESS", "FULL")?.ToUpper()) {
                    case "OMIT": ExportEbicsAddress = 0; break;
                    case "LINES": ExportEbicsAddress = 1; break;
                    case "FULL": ExportEbicsAddress = 2; break;
                }

                var autoAdjust = (parameters.GetValueOrDefault("AUTOADJUST_BUSINESSSHARES") ?? "").Split(';');
                AutoAdjustBs = autoAdjust.Length == 5 ? (
                    int.TryParse(autoAdjust[0], out var v1) ? v1 : null,
                    double.TryParse(autoAdjust[1], out var v2) ? v2 : null,
                    int.TryParse(autoAdjust[2], out var v3) ? v3 : null,
                    double.TryParse(autoAdjust[3], out var v4) ? v4 : null,
                    int.TryParse(autoAdjust[4], out var v5) ? v5 : null
                    ) : (null, null, null, null, null);
            } catch {
                throw new KeyNotFoundException();
            }
        }

        private IEnumerable<(string, string?)> GetParamValues() {
            string deliveryNoteStats = "SHORT";
            switch (ModeDeliveryNoteStats) {
                case 0: deliveryNoteStats = "NONE"; break;
                case 1: deliveryNoteStats = "GA_ONLY"; break;
                case 2: deliveryNoteStats = "SHORT"; break;
                case 3: deliveryNoteStats = "FULL"; break;
            }
            string modeWineQualityStatistics = "OE";
            switch (ModeWineQualityStatistics) {
                case 0: modeWineQualityStatistics = "OE"; break;
                case 1: modeWineQualityStatistics = "KMW/1"; break;
                case 2: modeWineQualityStatistics = "KMW/2"; break;
                case 3: modeWineQualityStatistics = "KMW/5"; break;
                case 4: modeWineQualityStatistics = "KMW/10"; break;
            }
            string orderingMemberList = "MGNR";
            switch (OrderingMemberList) {
                case 0: orderingMemberList = "MGNR"; break;
                case 1: orderingMemberList = "NAME"; break;
                case 2: orderingMemberList = "KG"; break;
            }
            string mailSendPostal = "WISH";
            switch (MailSendPostal) {
                case 0: mailSendPostal = "NONE"; break;
                case 1: mailSendPostal = "NO_EMAIL"; break;
                case 2: mailSendPostal = "WISH"; break;
                case 3: mailSendPostal = "ALL"; break;
            }
            string mailSendEmail = "WISH";
            switch (MailSendEmail) {
                case 0: mailSendEmail = "NONE"; break;
                case 1: mailSendEmail = "WISH"; break;
                case 2: mailSendEmail = "ALL"; break;
            }
            string mailOrdering = "MGNR";
            switch (MailOrdering) {
                case 0: mailOrdering = "MGNR"; break;
                case 1: mailOrdering = "NAME"; break;
                case 2: mailOrdering = "PLZ"; break;
            }
            string exportEbicsAddress = "FULL";
            switch (ExportEbicsAddress) {
                case 0: exportEbicsAddress = "OMIT"; break;
                case 1: exportEbicsAddress = "LINES"; break;
                case 2: exportEbicsAddress = "FULL"; break;
            }
            string autoAdjust = $"{AutoAdjustBs.AllowanceKg};{AutoAdjustBs.AllowanceBs?.ToString(CultureInfo.InvariantCulture)};" +
                                $"{AutoAdjustBs.AllowanceKgPerBs};{AutoAdjustBs.AllowancePercent?.ToString(CultureInfo.InvariantCulture)};" +
                                $"{AutoAdjustBs.MinBs}";
            return [
                ("CLIENT_NAME_TOKEN", NameToken),
                ("CLIENT_NAME_SHORT", NameShort),
                ("CLIENT_NAME", Name),
                ("CLIENT_NAME_SUFFIX", NameSuffix),
                ("CLIENT_NAME_TYPE", NameType),
                ("CLIENT_PLZ", Plz.ToString()),
                ("CLIENT_ORT", Ort),
                ("CLIENT_ADDRESS", Address),
                ("CLIENT_PHONE", PhoneNr),
                ("CLIENT_FAX", FaxNr),
                ("CLIENT_EMAIL", EmailAddress),
                ("CLIENT_WEBSITE", Website),
                ("CLIENT_LFBISNR", LfbisNr),
                ("CLIENT_USTIDNR", UstIdNr),
                ("CLIENT_BIC", Bic),
                ("CLIENT_IBAN", Iban),
                ("CLIENT_ORGANIC_AUTHORITY", OrganicAuthority),
                ("MODE_DELIVERYNOTE_STATS", deliveryNoteStats),
                ("MODE_WINEQUALITYSTATISTICS", modeWineQualityStatistics),
                ("ORDERING_MEMBERLIST", orderingMemberList),
                ("DOCUMENT_SENDER", Sender2),
                ("TEXT_DELIVERYNOTE", TextDeliveryNote),
                ("TEXT_DELIVERYCONFIRMATION", TextDeliveryConfirmation),
                ("TEXT_CREDITNOTE", TextCreditNote),
                ("TEXT_EMAIL_SUBJECT", TextEmailSubject),
                ("TEXT_EMAIL_BODY", TextEmailBody),
                ("MAIL_INCLUDE_NON_DELIVERERS", MailIncludeNonDeliverers ? "YES" : "NO"),
                ("MAIL_DOUBLE_PAGED", MailDoublePaged ? "YES" : "NO"),
                ("MAIL_SEND_POSTAL", mailSendPostal),
                ("MAIL_SEND_EMAIL", mailSendEmail),
                ("MAIL_ORDERING", mailOrdering),
                ("EXPORT_EBICS_VERSION", ExportEbicsVersion.ToString()),
                ("EXPORT_EBICS_ADDRESS", exportEbicsAddress),
                ("AUTOADJUST_BUSINESSSHARES", autoAdjust),
            ];
        }

        public async Task UpdateValues() {
            using var cnx = await AppDbContext.ConnectAsync();
            using var cmd = cnx.CreateCommand();
            var pv = GetParamValues();
            cmd.CommandText = "INSERT INTO client_parameter (param, value) VALUES " +
                string.Join(", ", pv.Select((pv, i) => $"(@p{i}, " + (pv.Item2 != null ? $"@v{i}" : "NULL") + ")")) +
                " ON CONFLICT DO UPDATE SET value = excluded.value";

            int i = 0;
            foreach (var (p, v) in pv) {
                cmd.Parameters.Add(new SqliteParameter($"@p{i}", p));
                if (v != null)
                    cmd.Parameters.Add(new SqliteParameter($"@v{i}", v));
                i++;
            }

            await cmd.ExecuteNonQueryAsync();
        }
    }
}
