using KarleyLibrary.Erweiterungen;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using TwinFinder.Matching;
using TwinFinder.Matching.Compare;
using TwinFinder.Matching.StringFuzzyCompare.AddressSpecific;
using TwinFinder.Matching.StringFuzzyCompare.Aggregators;

namespace WK5.Core.Models
{
    public class Ansprechpartner
    {
        #region Felder
        private string? _PART_WK5_A_NOTIZ;
        #endregion
        #region Datenbankfelder
        #region Alphanumerische Werte
        public string? PART_A_ADRESSTEXT { get; set; }
        public string? PART_A_BEREICH { get; set; }
        public string? PART_A_BRIEFANREDE { get; set; }
        public string PART_A_EMAIL { get; set; } = String.Empty;
        public string PART_A_KUNDNR { get; set; } = String.Empty;
        public string? PART_A_MOBIL { get; set; }
        public string PART_A_NAME { get; set; } = String.Empty;
        public string? PART_A_TELEFAX { get; set; }
        public string? PART_A_TELEFON { get; set; }
        public string? PART_A_VORNAME { get; set; } = String.Empty;
        #endregion
        #region Blob Werte
        public string? PART_B_NOTIZ { get; set; }
        #endregion
        #region Logische Werte
        public bool PART_L_LS_MAIL { get; set; }
        public bool PART_L_MAHN_MAIL { get; set; }
        public bool PART_L_MAILING { get; set; }
        public bool PART_L_RE_MAIL { get; set; }
        #endregion
        #region Numerische Werte
        public int PART_N_LASTUSER { get; set; }
        /// <summary>
        /// Ruft die Nummer des Ansprechpartners ab.
        /// <para>
        /// Der default Wert ist -1, da es in der W4 auch Ansprechpartner mit der ID 0 gibt.
        /// </para>
        /// </summary>
        public int PART_N_NR { get; set; } = -1;

        public int PART_N_FUNKTION { get; set; }
        #endregion
        #region Zeit Werte
        public DateTime PART_TIMESTAMP { get; set; }
        #endregion
        #region WK5 Zusätze
        public bool PART_WK5_L_HAUPT { get; set; }
        public string? PART_WK5_A_NOTIZ
        {
            get
            {
                if (_PART_WK5_A_NOTIZ is null)
                {
                    return null;
                }

                return StringErweiterung.ConvertEncoding(_PART_WK5_A_NOTIZ, Encoding.GetEncoding("ISO-8859-1"), Encoding.UTF8);
            }
            set
            {
                if (value is null)
                {
                    _PART_WK5_A_NOTIZ = null;
                    return;
                }
                value = value.Replace("€", "EUR");
                _PART_WK5_A_NOTIZ = StringErweiterung.ConvertEncoding(value, Encoding.GetEncoding("ISO-8859-1"), Encoding.UTF8);
            }
        }
        #endregion
        #endregion
        
        /// <summary>
        /// Lädt alle Ansprechpartner für eine bestimmte Kundennummer
        /// </summary>
        /// <param name="kundennummer"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async IAsyncEnumerable<Ansprechpartner> GetAnsprechpartnerAsync(string kundennummer, [EnumeratorCancellation] CancellationToken cancellationToken = default)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                using FbController2 fbController = new FbController2();
                fbController.AddParameter("@PART_A_KUNDNR", kundennummer);
                DataTable data = await fbController.SelectDataAsync("SELECT * FROM ANSPRECHPARTNER WHERE PART_A_KUNDNR = @PART_A_KUNDNR");
                foreach (DataRow row in data.Rows)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }
                    yield return ObjectErweiterung.DataRowZuObjekt(new Ansprechpartner(), row);
                }
            }
        }
        /// <summary>
        /// Holt einen Ansprechpartner über die Kundennummer und den Namen
        /// </summary>
        /// <param name="kundennummer">Die Kundennummer zu der der Ansprechpartner gehört</param>
        /// <param name="name">Der Name des Ansprechpartners</param>
        /// <returns>Gibt ein Objekt der Klasse <see cref="Ansprechpartner"/> zurück</returns>
        public static async Task<Ansprechpartner?> GetAnsprechpartnerByNameAsync(string kundennummer, string name)
        {
            List<Ansprechpartner> alle = await GetAnsprechpartnerAsync(kundennummer).ToListAsync();

            string explainPlan = String.Empty;
            float result = 0.0f;

            var record1 = new Dictionary<string, string>()
            {
                {"Name", name ?? String.Empty }
            };

            var defGroup = new CompareDefinitionGroup()
            {
                Aggregator = new MaximumAggregator(),

                CompareDefinitions = new List<CompareDefinition>()
                {
                    new CompareDefinition()
                    {
                        Name = "AddressDefinition",
                        Aggregator = new AverageAggregator(),
                        CompareFields = new List<CompareField>()
                        {
                            new CompareField()
                            {
                                Name1 = "Name",
                                Name2 = "Name",
                                FuzzyComparer = new NameComparer()
                            }
                        }
                    }
                }
            };

            foreach (Ansprechpartner partner in alle)
            {
                var record2 = new Dictionary<string, string>()
                {
                    {"Name", $"{partner.PART_A_VORNAME} {partner.PART_A_NAME}" }
                };

                result = MatchingService.Instance.CompareRecords(record1, record2, defGroup, out explainPlan);

                if (result >= 95)
                {
                    return partner;
                }
            }


            return null;
        }
        public static async Task DeleteAnsprechpartnerAsync(string PART_A_KUNDNR, int PART_N_NR, int userId)
        {
            using FbController2 fbController = new FbController2(userId);
            fbController.AddParameter("@PART_A_KUNDNR", PART_A_KUNDNR);
            fbController.AddParameter("@PART_N_NR", PART_N_NR);
            await fbController.QueryAsync(@"DELETE FROM ANSPRECHPARTNER WHERE PART_A_KUNDNR = @PART_A_KUNDNR AND PART_N_NR = @PART_N_NR");
        }
        public static async Task<Ansprechpartner> UpdateAnsprechpartnerAsync(Ansprechpartner partner, int userId)
        {
            if (String.IsNullOrWhiteSpace(partner.PART_A_KUNDNR))
            {
                throw new ArgumentException("Der Ansprechpartner verfügt über keine Kundennummer");
            }

            if (partner.PART_N_NR < 0)
            {
                throw new ArgumentException("Der Ansprechpartner verfügt über keine gültige PART_N_NR");
            }

            using FbController2 fbController = new FbController2(userId);

            fbController.AddParameter("@PART_A_VORNAME", partner.PART_A_VORNAME?.Trim() ?? String.Empty);
            fbController.AddParameter("@PART_A_NAME", partner.PART_A_NAME?.Trim() ?? String.Empty);
            fbController.AddParameter("@PART_A_BEREICH", partner.PART_A_BEREICH?.Trim());
            fbController.AddParameter("@PART_A_BRIEFANREDE", partner.PART_A_BRIEFANREDE?.Trim());
            fbController.AddParameter("@PART_A_TELEFON", partner.PART_A_TELEFON?.Trim());
            fbController.AddParameter("@PART_A_MOBIL", partner.PART_A_MOBIL?.Trim());
            fbController.AddParameter("@PART_A_EMAIL", partner.PART_A_EMAIL?.Trim());
            fbController.AddParameter("@PART_A_KUNDNR", partner.PART_A_KUNDNR);
            fbController.AddParameter("@PART_N_NR", partner.PART_N_NR);
            fbController.AddParameter("@PART_A_TELEFAX", partner.PART_A_TELEFAX?.Trim());
            fbController.AddParameter("@PART_WK5_L_HAUPT", partner.PART_WK5_L_HAUPT.ToFirebirdBool());
            fbController.AddParameter("@PART_WK5_A_NOTIZ", partner.PART_WK5_A_NOTIZ?.Trim());
            fbController.AddParameter("@PART_N_FUNKTION", partner.PART_N_FUNKTION);

            await fbController.QueryAsync(@"UPDATE ANSPRECHPARTNER SET 
PART_A_VORNAME = @PART_A_VORNAME, 
PART_A_NAME = @PART_A_NAME, 
PART_A_BEREICH = @PART_A_BEREICH,
PART_A_BRIEFANREDE = @PART_A_BRIEFANREDE, 
PART_A_TELEFON = @PART_A_TELEFON, 
PART_A_MOBIL = @PART_A_MOBIL,
PART_A_EMAIL = @PART_A_EMAIL, 
PART_A_TELEFAX = @PART_A_TELEFAX, 
PART_WK5_L_HAUPT = @PART_WK5_L_HAUPT,
PART_WK5_A_NOTIZ = @PART_WK5_A_NOTIZ,
PART_N_FUNKTION = @PART_N_FUNKTION
WHERE PART_A_KUNDNR = @PART_A_KUNDNR 
AND PART_N_NR = @PART_N_NR");
            return partner;
        }
        public static async Task<Ansprechpartner> CreateAnsprechpartnerAsync(Ansprechpartner partner, int userId)
        {

            if (String.IsNullOrWhiteSpace(partner.PART_A_KUNDNR))
            {
                throw new ArgumentException($"Es wurde keine Kundennummer in dem Ansprechpartner übergeben");
            }

            using FbController2 fbController = new FbController2(userId);


            // Es gibt ein Problem, wenn die Abfrage für die Partnernummer unten geschieht. Daher vorher
            fbController.AddParameter("@PART_A_KUNDNR", partner.PART_A_KUNDNR);
            var idObj = await fbController.FetchObjectAsync("SELECT CASE WHEN MAX(PART_N_NR) IS NULL THEN 0 ELSE MAX(PART_N_NR)END + 1 FROM ANSPRECHPARTNER WHERE PART_A_KUNDNR = @PART_A_KUNDNR");
            if (idObj is null)
            {
                throw new Exception("#WK5_ADD_ANSPRECHPARTNER - Es ist ein unbekannter Fehler beim einfügen des Ansprechpartners aufgetreten. Bitte der Programmierung Bescheid geben");
            }

            int PART_N_NR = Convert.ToInt32(idObj);
            partner.PART_N_NR = PART_N_NR;

            fbController.AddParameter("@PART_N_NR", PART_N_NR);
            fbController.AddParameter("@PART_A_KUNDNR", partner.PART_A_KUNDNR);
            fbController.AddParameter("@PART_A_NAME", partner.PART_A_NAME.Trim());
            fbController.AddParameter("@PART_A_VORNAME", partner.PART_A_VORNAME?.Trim());
            fbController.AddParameter("@PART_A_EMAIL", partner.PART_A_EMAIL?.Trim());
            fbController.AddParameter("@PART_A_TELEFON", partner.PART_A_TELEFON?.Trim() ?? String.Empty);
            fbController.AddParameter("@PART_A_BRIEFANREDE", partner.PART_A_BRIEFANREDE?.Trim() ?? String.Empty);
            fbController.AddParameter("@PART_A_ADRESSTEXT", partner.PART_A_ADRESSTEXT?.Trim() ?? String.Empty);
            fbController.AddParameter("@PART_A_BEREICH", partner.PART_A_BEREICH?.Trim());
            fbController.AddParameter("@PART_A_MOBIL", partner.PART_A_MOBIL?.Trim());
            fbController.AddParameter("@PART_A_TELEFAX", partner.PART_A_TELEFAX?.Trim());
            fbController.AddParameter("@PART_WK5_L_HAUPT", partner.PART_WK5_L_HAUPT.ToFirebirdBool());
            fbController.AddParameter("@PART_WK5_A_NOTIZ", partner.PART_WK5_A_NOTIZ?.Trim());
            fbController.AddParameter("@PART_N_FUNKTION", partner.PART_N_FUNKTION);
            await fbController.QueryAsync(@"INSERT INTO ANSPRECHPARTNER
(
PART_N_NR, PART_A_KUNDNR, PART_A_NAME, PART_A_VORNAME, 
PART_A_EMAIL, PART_TIMESTAMP, PART_A_TELEFON, PART_A_BRIEFANREDE, 
PART_A_ADRESSTEXT, PART_A_BEREICH, PART_A_MOBIL, PART_A_TELEFAX,
PART_WK5_L_HAUPT,PART_WK5_A_NOTIZ, PART_N_FUNKTION
) 
VALUES 
(
@PART_N_NR,@PART_A_KUNDNR, @PART_A_NAME, @PART_A_VORNAME, 
@PART_A_EMAIL, CURRENT_TIMESTAMP, @PART_A_TELEFON, @PART_A_BRIEFANREDE, 
@PART_A_ADRESSTEXT, @PART_A_BEREICH, @PART_A_MOBIL, @PART_A_TELEFAX,
@PART_WK5_L_HAUPT, @PART_WK5_A_NOTIZ, @PART_N_FUNKTION
) 
RETURNING PART_N_NR");

            return partner;
        }


        public string GetName() => Regex.Replace($"{PART_A_VORNAME} {PART_A_NAME}", "  +/g", " ").Trim();
    }
}
