using FirebirdSql.Data.FirebirdClient;
using KarleyLibrary.Attributes;
using KarleyLibrary.Erweiterungen;
using System;
using System.Collections.Generic;
using System.Data;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WK5.Core.Models.VatChecker;

namespace WK5.Core.Models
{
    public class Lieferanschrift : IVatCheckable
    {
        #region Felder
        private string? _kula_wk5_a_notiz;
        private string _kula_a_land = String.Empty;
        private string _kula_a_name1 = String.Empty;
        private string _kula_a_name2 = String.Empty;
        private string _kula_a_ort = String.Empty;
        private string _kula_a_plz = String.Empty;
        private string _kula_a_strasse = String.Empty;
        #endregion
        #region Datenbankfelder
        // Die Attribute spiegeln vollständig die Spalten des Table LIEFERANSCHRIFTEN der Datenbank wieder 
        #region Alphanumerische Werte
        public string? KULA_A_EMAIL { get; set; } = String.Empty;
        public string? KULA_A_FAX { get; set; } = String.Empty;
        public string? KULA_A_HAUSNR { get; set; }
        public string KULA_A_KUNDNR { get; set; } = String.Empty;
        public string KULA_A_LAND { get => _kula_a_land; set => _kula_a_land = value ?? String.Empty; }
        public string KULA_A_NAME1 { get => _kula_a_name1; set => _kula_a_name1 = value ?? String.Empty; }
        public string KULA_A_NAME2 { get => _kula_a_name2; set => _kula_a_name2 = value ?? String.Empty; }
        public string? KULA_A_NAME3 { get; set; } = String.Empty;
        public string KULA_A_ORT { get => _kula_a_ort; set => _kula_a_ort = value ?? String.Empty; }
        public string? KULA_A_PARTNER { get; set; }
        public string KULA_A_PLZ { get => _kula_a_plz; set => _kula_a_plz = value ?? String.Empty; }
        public string KULA_A_STRASSE { get => _kula_a_strasse; set => _kula_a_strasse = value ?? String.Empty; }
        public string? KULA_A_TEL { get; set; } = String.Empty;
        public string? KULA_A_USTID { get; set; } = String.Empty;
        #endregion
        #region Blob Werte
        public string? KULA_B_NOTIZ { get; set; }

        #endregion
        #region Logische Werte
        public bool KULA_L_MWST { get; set; }
        public bool KULA_L_MWSTABWEICHEND { get; set; }

        public bool KULA_L_INAKTIV { get; set; }
        #endregion
        #region Numerische Werte
        public int KULA_N_LASTUSER { get; set; }
        public int KULA_N_MWSTKENNUNG { get; set; }
        /// <summary>
        /// Ruft die ID der Lieferanschrift ab.
        /// <para>
        /// Der Default Wert ist -1, da es in der W4 auch Lieferanschriften mit der ID 0 gibt
        /// </para>
        /// </summary>
        public int KULA_N_NR { get; set; } = -1;
        #endregion
        #region Datum Werte
        public DateTime KULA_TIMESTAMP { get; set; }
        #endregion
        #region WK5 zusätze
        [CompareField("KULA_WK5_L_USTID_GUELTIG")]
        public bool IstUstIdGültig { get; set; }
        [CompareField("KULA_WK5_D_USTID_GEPRUEFT_DATE")]
        public DateTime UstIdLastPrüfung { get; set; }

        public bool KULA_WK5_L_HAUPTANSCHRIFT { get; set; }

        public string? KULA_WK5_A_NOTIZ
        {
            get
            {
                if (_kula_wk5_a_notiz is null)
                {
                    return null;
                }

                return StringErweiterung.ConvertEncoding(_kula_wk5_a_notiz, Encoding.GetEncoding("ISO-8859-1"), Encoding.UTF8);
            }
            set
            {
                if (value is null)
                {
                    _kula_wk5_a_notiz = null;
                    return;
                }
                value = value.Replace("€", "EUR");
                _kula_wk5_a_notiz = StringErweiterung.ConvertEncoding(value, Encoding.GetEncoding("ISO-8859-1"), Encoding.UTF8);
            }
        }
        #endregion
        #endregion


        public int Verwendet { get; set; }
        public bool WirdVerwendet => Verwendet > 0;

        /// <summary>
        /// Lädt alle Lieferanschriften für eine bestimmte Kundennummer
        /// </summary>
        /// <param name="kundennummer"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async IAsyncEnumerable<Lieferanschrift> GetLieferanschriftenAsync(string kundennummer, [EnumeratorCancellation] CancellationToken cancellationToken = default)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                using FbController2 fbController = new FbController2();
                fbController.AddParameter("@KUNDENNUMMER", kundennummer);

                DataTable data = await fbController.SelectDataAsync(@"SELECT 
LF.*, 
CASE WHEN COALESCE(K.KUND_N_ABWEICHLIEF, 0) = 0 THEN
    'N'
ELSE
    'Y'
END AS KULA_WK5_L_HAUPTANSCHRIFT,
(SELECT COUNT(*) FROM BELEGE WHERE BELE_A_KUNDENNR = KULA_A_KUNDNR AND BELE_N_LIEFADRESSE = KULA_N_NR) AS VERWENDET 
FROM LIEFERANSCHRIFTEN LF
LEFT JOIN KUNDEN K ON (LF.KULA_A_KUNDNR = K.KUND_A_NR AND LF.KULA_N_NR = K.KUND_N_ABWEICHLIEF)
WHERE KULA_A_KUNDNR = @KUNDENNUMMER");

                foreach (DataRow row in data.Rows)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }
                    yield return ObjectErweiterung.DataRowZuObjekt(new Lieferanschrift(), row);
                }
            }

        }

        /// <summary>
        /// Gibt eine Lieferanschrift anhand der Kundennummer und Lieferanschriftsnummer zurück
        /// </summary>
        /// <param name="kundennummer"></param>
        /// <param name="lieferanschriftsnummer"></param>
        /// <returns></returns>
        public static async Task<Lieferanschrift?> GetLieferanschriftAsync(string kundennummer, int lieferanschriftsnummer)
        {
            if(lieferanschriftsnummer is 0)
            {
                return null;
            }

            using FbController2 fbController = new FbController2();
            fbController.AddParameter("@KULA_A_KUNDNR", kundennummer);
            fbController.AddParameter("@KULA_N_NR", lieferanschriftsnummer);
            DataRow? row = await fbController.SelectRowAsync(@"SELECT FIRST 1 
LF.*, 
CASE WHEN COALESCE(K.KUND_N_ABWEICHLIEF, 0) = 0 THEN
    'N'
ELSE
    'Y'
END AS KULA_WK5_L_HAUPTANSCHRIFT,
(SELECT COUNT(*) FROM BELEGE WHERE BELE_A_KUNDENNR = KULA_A_KUNDNR AND BELE_N_LIEFADRESSE = KULA_N_NR) AS VERWENDET 
FROM LIEFERANSCHRIFTEN LF
LEFT JOIN KUNDEN K ON (LF.KULA_A_KUNDNR = K.KUND_A_NR AND LF.KULA_N_NR = K.KUND_N_ABWEICHLIEF)
WHERE KULA_A_KUNDNR = @KULA_A_KUNDNR AND KULA_N_NR = @KULA_N_NR");

            return row is null ? null : ObjectErweiterung.DataRowZuObjekt(new Lieferanschrift(), row);
        }


        /// <summary>
        /// Löscht eine Lieferanschrift aus der W4.
        /// <para>
        /// Eine Lieferanschrift kann nur dann gelöscht werden, wenn diese keinem Beleg zugeordnet ist.
        /// </para>
        /// </summary>
        /// <param name="KULA_A_KUNDNR">Die Kundennummer zu der die Lieferanschrift gehört.</param>
        /// <param name="KULA_N_NR">Die Nummer der Lieferanschrift, die gelöscht werden soll.</param>
        /// <returns></returns>
        /// <exception cref="FbException">Kann auftreten, wenn in der Syntax ein Fehler ist, oder die Datenbank nicht erreicht werden konnte.</exception>
        public static async Task<bool> DeleteLieferanschriftAsync(string KULA_A_KUNDNR, int KULA_N_NR)
        {
            using FbController2 fbController = new FbController2();
            fbController.AddParameter("@BELE_A_KUNDENNR", KULA_A_KUNDNR);
            fbController.AddParameter("@BELE_N_LIEFADRESSE", KULA_N_NR);
            var anzahlBenutztObj = await fbController.FetchObjectAsync("SELECT COUNT(*) FROM BELEGE WHERE BELE_A_KUNDENNR = @BELE_A_KUNDENNR AND BELE_N_LIEFADRESSE = @BELE_N_LIEFADRESSE");

            if (anzahlBenutztObj != null && int.TryParse(anzahlBenutztObj.ToString(), out int anzahlBenutzt) && anzahlBenutzt == 0)
            {
                fbController.AddParameter("@KULA_A_KUNDNR", KULA_A_KUNDNR);
                fbController.AddParameter("@KULA_N_NR", KULA_N_NR);
                await fbController.QueryAsync("DELETE FROM LIEFERANSCHRIFTEN WHERE KULA_A_KUNDNR = @KULA_A_KUNDNR AND KULA_N_NR = @KULA_N_NR");
                return true;
            }

            return false;
        }
        public static async Task<Lieferanschrift> UpdateLieferanschriftAsync(Lieferanschrift anschrift, int userId)
        {
            if (String.IsNullOrWhiteSpace(anschrift.KULA_A_KUNDNR))
            {
                throw new ArgumentException("Die Anschrift verfügt über keine Kundennummer.");
            }

            if (anschrift.KULA_N_NR < 0)
            {
                throw new ArgumentException("Die Anschrift verfügt über keine gültige KULA_N_NR.");
            }

            using FbController2 fbController = new FbController2(userId);
            fbController.AddParameter("@KULA_N_NR", anschrift.KULA_N_NR);
            fbController.AddParameter("@KULA_A_KUNDNR", anschrift.KULA_A_KUNDNR);
            fbController.AddParameter("@KULA_A_EMAIL", anschrift.KULA_A_EMAIL?.Trim());
            fbController.AddParameter("@KULA_A_FAX", anschrift.KULA_A_FAX?.Trim());
            fbController.AddParameter("@KULA_A_LAND", anschrift.KULA_A_LAND?.Trim());
            fbController.AddParameter("@KULA_A_NAME1", anschrift.KULA_A_NAME1.Trim());
            fbController.AddParameter("@KULA_A_NAME2", anschrift.KULA_A_NAME2.Trim());
            fbController.AddParameter("@KULA_A_NAME3", anschrift.KULA_A_NAME3?.Trim());
            fbController.AddParameter("@KULA_A_ORT", anschrift.KULA_A_ORT.Trim());
            fbController.AddParameter("@KULA_A_PLZ", anschrift.KULA_A_PLZ.Trim());
            fbController.AddParameter("@KULA_A_STRASSE", anschrift.KULA_A_STRASSE.Trim());
            fbController.AddParameter("@KULA_A_TEL", anschrift.KULA_A_TEL?.Trim());
            fbController.AddParameter("@KULA_A_PARTNER", anschrift.KULA_A_PARTNER?.Trim());
            fbController.AddParameter("@KULA_WK5_A_NOTIZ", anschrift.KULA_WK5_A_NOTIZ?.Trim());
            fbController.AddParameter("@KULA_N_LASTUSER", userId);

            await fbController.QueryAsync(@"UPDATE LIEFERANSCHRIFTEN SET
KULA_A_EMAIL = @KULA_A_EMAIL, KULA_A_FAX = @KULA_A_FAX, KULA_A_LAND = @KULA_A_LAND,
KULA_A_NAME1 = @KULA_A_NAME1, KULA_A_NAME2 = @KULA_A_NAME2, KULA_A_NAME3 = @KULA_A_NAME3,
KULA_A_STRASSE = @KULA_A_STRASSE, KULA_A_ORT = @KULA_A_ORT, KULA_A_PLZ = @KULA_A_PLZ, 
KULA_A_TEL = @KULA_A_TEL, KULA_A_PARTNER = @KULA_A_PARTNER, KULA_WK5_A_NOTIZ = @KULA_WK5_A_NOTIZ,
KULA_N_LASTUSER = @KULA_N_LASTUSER
WHERE KULA_N_NR = @KULA_N_NR AND KULA_A_KUNDNR = @KULA_A_KUNDNR");

            if (anschrift.KULA_WK5_L_HAUPTANSCHRIFT)
            {
                await SetHauptanschriftAsync(anschrift.KULA_N_NR, anschrift.KULA_A_KUNDNR, userId);
            }

            return anschrift;
        }
        public static async Task<Lieferanschrift> CreateLieferanschriftAsync(Lieferanschrift anschrift, int userId)
        {
            if (String.IsNullOrWhiteSpace(anschrift.KULA_A_KUNDNR))
            {
                throw new ArgumentException($"Es wurde keine Kundennummer in der Lieferanschrift übergeben");
            }

            using FbController2 fbController = new FbController2(userId);

            fbController.AddParameter("@KULA_A_KUNDNR", anschrift.KULA_A_KUNDNR);
            fbController.AddParameter("@KULA_A_NAME1", anschrift.KULA_A_NAME1?.Trim());
            fbController.AddParameter("@KULA_A_NAME2", anschrift.KULA_A_NAME2?.Trim());
            fbController.AddParameter("@KULA_A_NAME3", anschrift.KULA_A_NAME3?.Trim());
            fbController.AddParameter("@KULA_A_STRASSE", anschrift.KULA_A_STRASSE.Trim());
            fbController.AddParameter("@KULA_A_LAND", anschrift.KULA_A_LAND?.Trim());
            fbController.AddParameter("@KULA_A_PLZ", anschrift.KULA_A_PLZ?.Trim());
            fbController.AddParameter("@KULA_A_ORT", anschrift.KULA_A_ORT?.Trim());
            fbController.AddParameter("@KULA_A_EMAIL", anschrift.KULA_A_EMAIL?.Trim());
            fbController.AddParameter("@KULA_N_LASTUSER", userId);
            fbController.AddParameter("@KULA_L_MWSTABWEICHEND", anschrift.KULA_L_MWSTABWEICHEND ? "Y" : "N");
            fbController.AddParameter("@KULA_A_USTID", anschrift.KULA_A_USTID?.Trim());
            fbController.AddParameter("@KULA_A_TEL", anschrift.KULA_A_TEL?.Trim());
            fbController.AddParameter("@KULA_A_FAX", anschrift.KULA_A_FAX?.Trim());
            fbController.AddParameter("@KULA_A_PARTNER", anschrift.KULA_A_PARTNER?.Trim());
            fbController.AddParameter("@KULA_WK5_A_NOTIZ", anschrift.KULA_WK5_A_NOTIZ?.Trim());


            object? idObj = await fbController.FetchObjectAsync(@"
INSERT INTO LIEFERANSCHRIFTEN (
KULA_N_NR, KULA_A_KUNDNR, KULA_A_NAME1, KULA_A_NAME2, KULA_A_NAME3,
KULA_A_STRASSE, KULA_A_LAND, KULA_A_PLZ, KULA_A_ORT, KULA_A_EMAIL, 
KULA_TIMESTAMP, KULA_N_LASTUSER, KULA_L_MWSTABWEICHEND, KULA_A_USTID,
KULA_A_TEL, KULA_A_FAX, KULA_A_PARTNER, KULA_WK5_A_NOTIZ
)
VALUES
(
(SELECT CASE WHEN MAX(KULA_N_NR) IS NULL THEN 0 ELSE MAX(KULA_N_NR)END + 1 FROM LIEFERANSCHRIFTEN WHERE KULA_A_KUNDNR = @KULA_A_KUNDNR), @KULA_A_KUNDNR, @KULA_A_NAME1, @KULA_A_NAME2, @KULA_A_NAME3, 
@KULA_A_STRASSE, @KULA_A_LAND, @KULA_A_PLZ, @KULA_A_ORT, @KULA_A_EMAIL, 
CURRENT_TIMESTAMP, @KULA_N_LASTUSER, @KULA_L_MWSTABWEICHEND, @KULA_A_USTID,
@KULA_A_TEL, @KULA_A_FAX, @KULA_A_PARTNER, @KULA_WK5_A_NOTIZ
) 
RETURNING KULA_N_NR");

            if (idObj is null)
            {
                throw new Exception("#WK5_ADD_LIEFERANSCHRIFT - Es ist ein unbekannter Fehler beim einfügen der Lieferanschrift aufgetreten. Bitte der Programmierung Bescheid geben");
            }

            if (int.TryParse(idObj?.ToString(), out int KULA_N_NR))
            {
                anschrift.KULA_N_NR = KULA_N_NR;
            }

            if (anschrift.KULA_WK5_L_HAUPTANSCHRIFT)
            {
                await SetHauptanschriftAsync(KULA_N_NR, anschrift.KULA_A_KUNDNR, userId);
            }

            return anschrift;
        }

        public static async Task SetHauptanschriftAsync(int KULA_N_NR, string KULA_A_KUNDNR, int userId)
        {
            using FbController2 fbController = new FbController2(userId);
            fbController.AddParameter("@KUND_N_ABWEICHLIEF", KULA_N_NR);
            fbController.AddParameter("@KUND_A_NR", KULA_A_KUNDNR);
            await fbController.QueryAsync("UPDATE KUNDEN SET KUND_N_ABWEICHLIEF = @KUND_N_ABWEICHLIEF WHERE KUND_A_NR = @KUND_A_NR");
        }


        public override string ToString() => $"{KULA_A_NAME1} {KULA_A_NAME2} - {KULA_A_STRASSE} - {KULA_A_LAND} - {KULA_A_PLZ} {KULA_A_ORT}";

        public VatRequest ToVatRequest(string ustIdNr)
        {
            return new VatRequest(ustIdNr, KULA_A_NAME1, KULA_A_ORT, KULA_A_PLZ, KULA_A_STRASSE, KULA_A_KUNDNR, false, VatZuordnung.Lieferanschrift, KULA_N_NR);
        }
    }
}

