using KarleyLibrary.Attributes;
using KarleyLibrary.Erweiterungen;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;

namespace WK5.Core.Models
{
    /// <summary>
    /// Stellt eine Zahlungsbedingung eines Kunden oder eines Beleges aus der W4 dar.
    /// </summary>
    public class Zahlungsbedingung
    {
        #region Datenbankfelder
        #region Alphanumerische Werte
        public string? ZABD_A_FORMULTEXT { get; set; }
        public string? ZABD_A_LAND { get; set; }
        public string? ZABD_A_NAME1 { get; set; }
        public string? ZABD_A_NAME2 { get; set; }
        public string? ZABD_A_ORT { get; set; }
        public string? ZABD_A_PLZ { get; set; }
        public string? ZABD_A_STRASSE { get; set; }
        public string ZABD_A_TEXT1 { get; set; } = String.Empty;
        public string? ZABD_A_TEXT2 { get; set; }
        /// <summary>
        /// Ruft den Payment Code für den Online Shop ab.
        /// </summary>
        public string? ZABD_A_CODE { get; set; }
        #endregion
        #region Logische Werte
        public bool ZABD_L_ABBUCHUNG { get; set; }
        public bool ZABD_L_ADRESSE { get; set; }
        public bool ZABD_L_BARZAHLUNG { get; set; }
        public bool ZABD_L_FACTORING { get; set; }
        public bool ZABD_L_RECHNUNG { get; set; }
        public bool ZABD_L_INAKTIV { get; set; }
        public bool ZABD_L_LEASING { get; set; }
        public bool ZABD_L_OP0 { get; set; }
        public bool ZABD_L_SCHECK { get; set; }
        public bool ZABD_L_VORKASSE { get; set; }
        public bool WK5_ZABD_L_BRAUCHT_KONTODATEN { get; set; }
        #endregion
        #region Numerische Werte
        public decimal ZABD_N_ABSCHLAGEK { get; set; }
        public decimal ZABD_N_ABSCHLAGVK { get; set; }
        public decimal ZABD_N_AUFSCHLAGEK { get; set; }
        public decimal ZABD_N_AUFSCHLAGVK { get; set; }
        public int ZABD_N_KUNDE_LIEF { get; set; }
        public int ZABD_N_LASTUSER { get; set; }
        public int ZABD_N_NETTOTAGE { get; set; }
        public int ZABD_N_NR { get; set; }
        public int ZABD_N_RISIKOLEVEL { get; set; }
        public int ZABD_N_SKONTO1PROZ { get; set; }
        public int ZABD_N_SKONTO1TAGE { get; set; }
        public int ZABD_N_SKONTO2PROZ { get; set; }
        public int ZABD_N_SKONTO2TAGE { get; set; }
        public int ZABD_N_ZAHLMONAT { get; set; }
        public int ZABD_N_ZAHLTAG { get; set; }
        public int ZABD_NR_ALPHA { get; set; }
        #endregion
        #region Datums Werte
        public DateTime ZABD_TIMESTAMP { get; set; }
        #endregion
        #endregion

        /// <summary>
        /// Gibt eine Liste aller Zahlungsbedingungen zurück
        /// </summary>
        public static async Task<ZahlungsbedingungCollection> GetZahlungsbedingungenAsync(FbController2 fbController)
        {            
            DataTable data = await fbController.SelectDataAsync("SELECT * FROM ZAHLUNGSBEDINGUNG ORDER BY ZABD_A_TEXT1");
            var collection = new ZahlungsbedingungCollection();
            foreach (DataRow row in data.Rows)
            {
                collection.Add(ObjectErweiterung.DataRowZuObjekt(new Zahlungsbedingung(), row));
            }

            return collection;
        }

        /// <summary>
        /// Gib eine Zahlungsbedingung anhand ihrer ID zurück
        /// </summary>
        /// <param name="ZABD_N_NR">Die Id der zu suchenden Zahlungsbedingung</param>
        /// <returns>Gibt ein Objekt der Klasse <see cref="Zahlungsbedingung"/> zurück</returns>
        public static async Task<Zahlungsbedingung?> GetZahlungsbedingungAsync(int ZABD_N_NR)
        {
            using FbController2 fbController = new FbController2();
            fbController.AddParameter("@ZABD_N_NR", ZABD_N_NR);
            var row = await fbController.SelectRowAsync("SELECT * FROM ZAHLUNGSBEDINGUNG WHERE ZABD_N_NR = @ZABD_N_NR");

            return row is null ? null : ObjectErweiterung.DataRowZuObjekt(new Zahlungsbedingung(), row);
        }
        public override string ToString() => ZABD_A_TEXT1 ?? "";

        public decimal GetSkonto(decimal wert, DateTime beleg, DateTime zahl)
        {
            decimal skonto = wert;

            if(beleg.AddDays(ZABD_N_SKONTO1TAGE) <= zahl)
            {
                skonto *= (ZABD_N_SKONTO1PROZ / 100);
            }

            if (beleg.AddDays(ZABD_N_SKONTO2TAGE) <= zahl)
            {
                skonto *= (ZABD_N_SKONTO2PROZ / 100);
            }

            return skonto;
        }
    }

    /// <summary>
    /// Stellt eine Collection von verfügbaren Zahlungsbedinungen dar.
    /// </summary>
    public class ZahlungsbedingungCollection : List<Zahlungsbedingung>
    {
        #region Konstruktoren
        public ZahlungsbedingungCollection()
        {

        }

        public ZahlungsbedingungCollection(Zahlungsbedingung[] zahlungsbedingungen) => this.AddRange(zahlungsbedingungen);

        public ZahlungsbedingungCollection(List<Zahlungsbedingung> zahlungsbedingungen) => this.AddRange(zahlungsbedingungen);
        #endregion
        /// <summary>
        /// Ruft eine Zahlungsbedindung anhand seiner eindeutigen ID ab.
        /// </summary>
        /// <param name="ZABD_N_NR"></param>
        /// <returns>Eine <see cref="Zahlungsbedingung"/>, sofern eine anhand der ID gefunden wurde, ansonsten null.</returns>
        public new Zahlungsbedingung? this[int ZABD_N_NR] => this.FirstOrDefault(z => z.ZABD_N_NR == ZABD_N_NR);


        /// <summary>
        /// Ruft alle aktiven Zahlungsbedingungen für Kunden ab.
        /// </summary>
        /// <param name="benötigteId">
        /// Die ID einer Zahlungsbedingung, die ggf. benötigt wird, obwohl Sie inaktiv ist. Wenn keine besondere Zahlungsbedingung benötigt wird, dann kann hier 0 übergeben werden.
        /// </param>
        /// <returns></returns>
        public IEnumerable<Zahlungsbedingung> GetKundenZahlungsbedingungen(int benötigteId)
        {
            List<Zahlungsbedingung> zahlungsbedingungen = this.Where(x => x.ZABD_N_KUNDE_LIEF is 0 or 1 && !x.ZABD_L_INAKTIV).ToList();

            if (benötigteId > 0)
            {
                bool benötigteZahlungsbedingungExistiert = zahlungsbedingungen.Where(x => x.ZABD_N_NR == benötigteId).Any();

                if (!benötigteZahlungsbedingungExistiert)
                {
                    zahlungsbedingungen.Add(GetZahlungsbedingungById(benötigteId));
                }
            }

            foreach (var zahlungsbedingung in zahlungsbedingungen)
            {
                yield return zahlungsbedingung;
            }

        }
        /// <summary>
        /// Ruft alle aktiven Zahlungsbedingungen für Lieferanten ab.
        /// </summary>
        /// <param name="benötigteId">
        /// Die ID einer Zahlungsbedingung, die ggf. benötigt wird, obwohl Sie inaktiv ist. Wenn keine besondere Zahlungsbedingung benötigt wird, dann kann hier 0 übergeben werden.
        /// </param>
        /// <returns></returns>
        public IEnumerable<Zahlungsbedingung> GetLieferantenZahlungsbedingungen(int benötigteId)
        {
            List<Zahlungsbedingung> zahlungsbedingungen = this.Where(x => x.ZABD_N_KUNDE_LIEF is 0 or 2 && !x.ZABD_L_INAKTIV).ToList();

            if (benötigteId > 0)
            {
                bool benötigteZahlungsbedingungExistiert = zahlungsbedingungen.Where(x => x.ZABD_N_NR == benötigteId).Any();

                if (!benötigteZahlungsbedingungExistiert)
                {
                    zahlungsbedingungen.Add(GetZahlungsbedingungById(benötigteId));
                }
            }

            foreach (var zahlungsbedingung in zahlungsbedingungen)
            {
                yield return zahlungsbedingung;
            }
        }

        public IEnumerable<Zahlungsbedingung> GetRechnungsZahlungsbedingungen()
        {
            return this.Where(x => x.ZABD_L_RECHNUNG);
        }

        public IEnumerable<Zahlungsbedingung> GetRechnungsUndLastschriftZahlungsbedingungen()
        {
            return this.Where(x => x.ZABD_L_RECHNUNG || x.ZABD_L_ABBUCHUNG);
        }
        public IEnumerable<Zahlungsbedingung> GetVorkasseZahlungsbedingungen()
        {
            return this.Where(x => x.ZABD_L_VORKASSE);
        }

        private Zahlungsbedingung GetZahlungsbedingungById(int id)
        {
            Zahlungsbedingung? zahlungsbedingung = this[id];

            if (zahlungsbedingung is not null)
            {
                return zahlungsbedingung;
            }
            else
            {
                return new Zahlungsbedingung
                {
                    ZABD_A_TEXT1 = "Unbekannte",
                    ZABD_N_NR = id
                };
            }
        }

        public bool IstRechnung(int zahlungsbedingungId)
        {
            Zahlungsbedingung? zahlungsbedinung = this[zahlungsbedingungId];
            if (zahlungsbedinung is null)
            {
                return false;
            }

            return zahlungsbedinung.ZABD_L_RECHNUNG;
        }

        public bool IstVorkasse(int zahlungsbedingungId)
        {
            Zahlungsbedingung? zahlungsbedinung = this[zahlungsbedingungId];
            if (zahlungsbedinung is null)
            {
                return false;
            }

            return zahlungsbedinung.ZABD_L_VORKASSE;
        }

        public bool IstLastschrift(int zahlungsbedingungId)
        {
            Zahlungsbedingung? zahlungsbedinung = this[zahlungsbedingungId];
            if (zahlungsbedinung is null)
            {
                return false;
            }

            return zahlungsbedinung.ZABD_L_ABBUCHUNG;
        }

        public bool BenötigtKontodaten(int zahlungsbedingungId)
        {
            Zahlungsbedingung? zahlungsbedinung = this[zahlungsbedingungId];
            if (zahlungsbedinung is null)
            {
                return false;
            }

            return zahlungsbedinung.WK5_ZABD_L_BRAUCHT_KONTODATEN;
        }
    }
}
