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;

namespace WK5.Core.Models
{
    /// <summary>
    /// Spiegelt die Datenbanktabelle RECHNUNGSANSCHRIFT wieder
    /// </summary>
    public class Rechnungsanschrift
    {
        #region Felder
        private string? _kure_wk5_a_notiz;
        private string _kure_a_land = String.Empty;
        private string _kure_a_name1 = String.Empty;
        private string _kure_a_name2 = String.Empty;
        private string _kure_a_ort = String.Empty;
        private string _kure_a_plz = String.Empty;
        private string _kure_a_strasse = String.Empty;
        private string _kure_a_kundnr = String.Empty;
        #endregion
        #region Datenbankfelder
        #region Alphanumerische Werte
        public string? KURE_A_EMAIL { get; set; } = String.Empty;
        public string? KURE_A_FAX { get; set; }
        public string KURE_A_KUNDNR { get => _kure_a_kundnr; set => _kure_a_kundnr = value ?? String.Empty; }
        public string KURE_A_LAND { get => _kure_a_land; set => _kure_a_land = value ?? String.Empty; }
        public string KURE_A_NAME1 { get => _kure_a_name1; set => _kure_a_name1 = value ?? String.Empty; }
        public string KURE_A_NAME2 { get => _kure_a_name2; set => _kure_a_name2 = value ?? String.Empty; }
        public string? KURE_A_NAME3 { get; set; } = String.Empty;
        public string KURE_A_ORT { get => _kure_a_ort; set => _kure_a_ort = value ?? String.Empty; }
        public string? KURE_A_PARTNER { get; set; }
        public string KURE_A_PLZ { get => _kure_a_plz; set => _kure_a_plz = value ?? String.Empty; }
        public string KURE_A_STRASSE { get => _kure_a_strasse; set => _kure_a_strasse = value ?? String.Empty; }
        public string? KURE_A_TEL { get; set; } = String.Empty;
        #endregion
        #region Blob Werte
        public string? KURE_B_NOTIZ { get; set; }
        #endregion
        #region Numerische Werte
        public int KURE_N_LASTUSER { get; set; }
        /// <summary>
        /// Ruft die ID der Rechnungsanschrift ab.
        /// <para>
        /// Der Default Wert ist -1, da es in der W4 auch Rechnungsanschriften mit der ID 0 gibt
        /// </para>
        /// </summary>
        public int KURE_N_NR { get; set; } = -1;
        #endregion
        #region Datums Werte
        public DateTime KURE_TIMESTAMP { get; set; }
        #endregion
        #region Logische Werte
        public bool KURE_L_LEERE_EMAIL { get; set; }
        #endregion
        #endregion
        #region WK5 Zusätze
        public bool KURE_WK5_L_HAUPTANSCHRIFT { get; set; }
        public string? KURE_WK5_A_NOTIZ
        {
            get
            {
                if (_kure_wk5_a_notiz is null)
                {
                    return null;
                }

                return StringErweiterung.ConvertEncoding(_kure_wk5_a_notiz, Encoding.GetEncoding("ISO-8859-1"), Encoding.UTF8);
            }
            set
            {
                if (value is null)
                {
                    _kure_wk5_a_notiz = null;
                    return;
                }
                value = value.Replace("€", "EUR");
                _kure_wk5_a_notiz = StringErweiterung.ConvertEncoding(value, Encoding.GetEncoding("ISO-8859-1"), Encoding.UTF8);
            }
        }
        #endregion
        public int Verwendet { get; set; }
        public bool WirdVerwendet => Verwendet > 0;

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

                DataTable data = await fbController.SelectDataAsync(@"SELECT 
KR.*, 
CASE WHEN COALESCE(K.KUND_N_ABWEICH_RECHN, 0) = 0 THEN
    'N'
ELSE
    'Y'
END AS KURE_WK5_L_HAUPTANSCHRIFT,
(SELECT COUNT(*) FROM BELEGE WHERE BELE_A_KUNDENNR = KURE_A_KUNDNR AND BELE_N_RECHADRESSE = KURE_N_NR) AS VERWENDET 
FROM KUNDENRECHNANSCHR KR
LEFT JOIN KUNDEN K ON (KR.KURE_A_KUNDNR = K.KUND_A_NR AND KR.KURE_N_NR = K.KUND_N_ABWEICH_RECHN)
WHERE KURE_A_KUNDNR = @KUNDENNUMMER");

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

        }

        /// <summary>
        /// Gibt eine Rechnungsanschrift auf Basis der Kundennummer und Rechnungsanschrifts Id zurück
        /// </summary>
        /// <param name="kundennummer">Die Kundennummer der zu suchenden Rechnungsanschrift</param>
        /// <param name="rechnungsanschriftnummer">Die Id der zu suchenden Rechnungsanschrift</param>
        /// <returns>Gibt ein Objetk der Klasse <see cref="Rechnungsanschrift"/> zurück</returns>
        public static async Task<Rechnungsanschrift?> GetRechnungsanschriftAsync(string kundennummer, int rechnungsanschriftnummer)
        {

            using FbController2 fbController = new FbController2();
            fbController.AddParameter("@KUNDENNNUMMER", kundennummer);
            fbController.AddParameter("@RECHNUNGSANSCHRIFTNUMMER", rechnungsanschriftnummer);
            var row = await fbController.SelectRowAsync(@"SELECT FIRST 1 
KR.*, 
CASE WHEN COALESCE(K.KUND_N_ABWEICH_RECHN, 0) = 0 THEN
    'N'
ELSE
    'Y'
END AS KURE_WK5_L_HAUPTANSCHRIFT,
(SELECT COUNT(*) FROM BELEGE WHERE BELE_A_KUNDENNR = KURE_A_KUNDNR AND BELE_N_RECHADRESSE = KURE_N_NR) AS VERWENDET 
FROM KUNDENRECHNANSCHR KR
LEFT JOIN KUNDEN K ON (KR.KURE_A_KUNDNR = K.KUND_A_NR AND KR.KURE_N_NR = K.KUND_N_ABWEICH_RECHN)
WHERE KURE_A_KUNDNR = @KUNDENNNUMMER AND KURE_N_NR = @RECHNUNGSANSCHRIFTNUMMER");

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



        /// <summary>
        /// Löscht eine Rechnungsanschrift aus der W4.
        /// <para>
        /// Eine Rechnungsanschrift kann nur dann gelöscht werden, wenn diese keinem Beleg zugeordnet ist.
        /// </para>
        /// </summary>
        /// <param name="KURE_A_KUNDNR">Die Kundennummer zu der die Lieferanschrift gehört.</param>
        /// <param name="KURE_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> DeleteRechnungsanschriftAsync(string KURE_A_KUNDNR, int KURE_N_NR, int userId)
        {
            using FbController2 fbController = new FbController2(userId);

            fbController.AddParameter("@BELE_A_KUNDENNR", KURE_A_KUNDNR);
            fbController.AddParameter("@BELE_N_RECHADRESSE", KURE_N_NR);
            var anzahlBenutztObj = await fbController.FetchObjectAsync("SELECT COUNT(*) FROM BELEGE WHERE BELE_A_KUNDENNR = @BELE_A_KUNDENNR AND BELE_N_RECHADRESSE = @BELE_N_RECHADRESSE");

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

            return false;
        }

        public static async Task<Rechnungsanschrift> UpdateRechnungsanschriftAsync(Rechnungsanschrift anschrift, int userId)
        {
            if (String.IsNullOrWhiteSpace(anschrift.KURE_A_KUNDNR))
            {
                throw new ArgumentException("Die Anschrift verfügt über keine Kundennummer.");
            }

            if (anschrift.KURE_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("@KURE_N_NR", anschrift.KURE_N_NR);
            fbController.AddParameter("@KURE_A_KUNDNR", anschrift.KURE_A_KUNDNR);
            fbController.AddParameter("@KURE_A_EMAIL", anschrift.KURE_A_EMAIL);
            fbController.AddParameter("@KURE_A_FAX", anschrift.KURE_A_FAX);
            fbController.AddParameter("@KURE_A_LAND", anschrift.KURE_A_LAND);
            fbController.AddParameter("@KURE_A_NAME1", anschrift.KURE_A_NAME1);
            fbController.AddParameter("@KURE_A_NAME2", anschrift.KURE_A_NAME2);
            fbController.AddParameter("@KURE_A_NAME3", anschrift.KURE_A_NAME3);
            fbController.AddParameter("@KURE_A_ORT", anschrift.KURE_A_ORT);
            fbController.AddParameter("@KURE_A_PLZ", anschrift.KURE_A_PLZ);
            fbController.AddParameter("@KURE_A_STRASSE", anschrift.KURE_A_STRASSE);
            fbController.AddParameter("@KURE_A_TEL", anschrift.KURE_A_TEL);
            fbController.AddParameter("@KURE_A_PARTNER", anschrift.KURE_A_PARTNER);
            fbController.AddParameter("@KURE_WK5_A_NOTIZ", anschrift.KURE_WK5_A_NOTIZ);
            fbController.AddParameter("@KURE_N_LASTUSER", userId);

            await fbController.QueryAsync(@"UPDATE KUNDENRECHNANSCHR SET
KURE_A_EMAIL = @KURE_A_EMAIL, KURE_A_FAX = @KURE_A_FAX, KURE_A_LAND = @KURE_A_LAND,
KURE_A_NAME1 = @KURE_A_NAME1, KURE_A_NAME2 = @KURE_A_NAME2, KURE_A_NAME3 = @KURE_A_NAME3,
KURE_A_STRASSE = @KURE_A_STRASSE, KURE_A_ORT = @KURE_A_ORT, KURE_A_PLZ = @KURE_A_PLZ, 
KURE_A_TEL = @KURE_A_TEL, KURE_A_PARTNER = @KURE_A_PARTNER,
KURE_WK5_A_NOTIZ = @KURE_WK5_A_NOTIZ, KURE_N_LASTUSER = @KURE_N_LASTUSER
WHERE KURE_N_NR = @KURE_N_NR AND KURE_A_KUNDNR = @KURE_A_KUNDNR");

            if (anschrift.KURE_WK5_L_HAUPTANSCHRIFT)
            {
                await SetHauptanschriftAsync(anschrift.KURE_N_NR, anschrift.KURE_A_KUNDNR, userId);
            }

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

            using FbController2 fbController = new FbController2(userId);
            fbController.AddParameter("@KURE_A_KUNDNR", anschrift.KURE_A_KUNDNR);
            fbController.AddParameter("@KURE_A_NAME1", anschrift.KURE_A_NAME1);
            fbController.AddParameter("@KURE_A_NAME2", anschrift.KURE_A_NAME2);
            fbController.AddParameter("@KURE_A_NAME3", anschrift.KURE_A_NAME3);
            fbController.AddParameter("@KURE_A_STRASSE", anschrift.KURE_A_STRASSE);
            fbController.AddParameter("@KURE_A_LAND", anschrift.KURE_A_LAND);
            fbController.AddParameter("@KURE_A_PLZ", anschrift.KURE_A_PLZ);
            fbController.AddParameter("@KURE_A_ORT", anschrift.KURE_A_ORT);
            fbController.AddParameter("@KURE_A_TEL", anschrift.KURE_A_TEL);
            fbController.AddParameter("@KURE_A_FAX", anschrift.KURE_A_FAX ?? String.Empty);
            fbController.AddParameter("@KURE_A_EMAIL", anschrift.KURE_A_EMAIL);
            fbController.AddParameter("@KURE_A_PARTNER", anschrift.KURE_A_PARTNER ?? String.Empty);
            fbController.AddParameter("@KURE_B_NOTIZ", anschrift.KURE_B_NOTIZ ?? String.Empty);
            fbController.AddParameter("@KURE_N_LASTUSER", userId);
            fbController.AddParameter("@KURE_WK5_A_NOTIZ", anschrift.KURE_WK5_A_NOTIZ);

            object? idObj = await fbController.FetchObjectAsync(@"INSERT INTO KUNDENRECHNANSCHR
(
KURE_N_NR, KURE_A_KUNDNR, KURE_A_NAME1, KURE_A_NAME2, KURE_A_NAME3, 
KURE_A_STRASSE, KURE_A_LAND, KURE_A_PLZ, KURE_A_ORT, KURE_A_TEL, 
KURE_A_FAX, KURE_A_EMAIL, KURE_A_PARTNER, KURE_B_NOTIZ, KURE_TIMESTAMP, 
KURE_N_LASTUSER, KURE_WK5_A_NOTIZ
)
VALUES
(
(SELECT CASE WHEN MAX(KURE_N_NR) IS NULL THEN 0 ELSE MAX(KURE_N_NR)END + 1 FROM KUNDENRECHNANSCHR WHERE KURE_A_KUNDNR = @KURE_A_KUNDNR), @KURE_A_KUNDNR, @KURE_A_NAME1, @KURE_A_NAME2, @KURE_A_NAME3,
@KURE_A_STRASSE, @KURE_A_LAND, @KURE_A_PLZ, @KURE_A_ORT, @KURE_A_TEL, 
@KURE_A_FAX, @KURE_A_EMAIL, @KURE_A_PARTNER, @KURE_B_NOTIZ, CURRENT_TIMESTAMP, 
@KURE_N_LASTUSER, @KURE_WK5_A_NOTIZ
) 
RETURNING KURE_N_NR");

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

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

            if (anschrift.KURE_WK5_L_HAUPTANSCHRIFT)
            {
                await SetHauptanschriftAsync(KURE_N_NR, anschrift.KURE_A_KUNDNR, userId);
            }

            return anschrift;
        }

        public static async Task SetHauptanschriftAsync(int KUND_N_ABWEICH_RECHN, string KULA_A_KUNDNR, int userId)
        {
            using FbController2 fbController = new FbController2(userId);

            fbController.AddParameter("@KUND_N_ABWEICH_RECHN", KUND_N_ABWEICH_RECHN);
            fbController.AddParameter("@KUND_A_NR", KULA_A_KUNDNR);
            await fbController.QueryAsync("UPDATE KUNDEN SET KUND_N_ABWEICH_RECHN = @KUND_N_ABWEICH_RECHN WHERE KUND_A_NR = @KUND_A_NR");
        }

        public override string ToString() => $"{KURE_A_NAME1} {KURE_A_NAME2} - {KURE_A_STRASSE} - {KURE_A_PLZ} {KURE_A_ORT}";
    }
}
