using KarleyLibrary.Erweiterungen;
using System;
using System.Collections.Generic;
using System.Data;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using WK5.Core.Basis.Filter;
using WK5.Core.Models;

namespace WK5.Core.Services
{
    //TODO: (Juri), Nochmal überarbeiten und auf Filter mit Phasensuche umstellen
    public class LieferantenService
    {
        /// <summary>
        /// Lädt sich alle Lieferanten die auf den gegebenen Filter zutreffen
        /// </summary>
        /// <param name="filter">Der Filter über den die Lieferanten gefiltert werden sollen</param>
        /// <returns>Gibt ein <see cref="IAsyncEnumerable{T}"/> of <see cref="Lieferant"/> zurück</returns>
        public async IAsyncEnumerable<Lieferant> GetLieferantenAsync(LieferantenFilter filter, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                using FbController2 fbController = new FbController2();
                var data = await fbController.SelectDataAsync(filter.ToSqlQuery(fbController));
                foreach (DataRow row in data.Rows)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }
                    yield return ObjectErweiterung.DataRowZuObjekt(new Lieferant(), row);
                }
            }
        }

        public async Task UpdateWichtigkeitAsync(int lieferantenId)
        {
            using FbController2 fbController = new FbController2();
            fbController.AddParameter("@INP_LIEFERANTENNUMMER", lieferantenId);
            await fbController.RunProcedureAsync("UPDATE_WICHTIGKEIT_LIEFERANT");
        }

        /// <summary>
        /// Lädt sich die Anzahl aller Lieferanten die auf einen Suchbegriff passen
        /// </summary>
        /// <param name="suchbegriff">Der Suchbegriff nach dem die Lieferanten gesucht werden sollen</param>
        /// <returns>Gibt einen <see cref="Task"/> of <see cref="int"/> zurück</returns>
        public async Task<int> GetAnzahlLieferantenAsync(LieferantenFilter filter, CancellationToken cancellationToken)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                using FbController2 fbController = new FbController2();

                var anzahlObj = await fbController.FetchObjectAsync(filter.ToCountQuery(fbController));

                if (anzahlObj is null)
                {
                    return 0;
                }

                _ = int.TryParse(anzahlObj.ToString(), out int anzahl);

                return anzahl;
            }

            return 0;
        }


        /// <summary>
        /// Lädt sich asynchron die Lieferanten Details aus der Datenbank
        /// </summary>
        /// <param name="LIEF_N_NR">Die Lieferantennummer zu der die Details geladen werden sollen</param>
        /// <returns>Gibt einen <see cref="Task"/> of <see cref="LieferantenDetails"/> zurück</returns>
        public async Task<LieferantenDetails?> GetLieferantenDetailsAsync(int LIEF_N_NR)
        {
            using FbController2 fbController = new FbController2();
            fbController.AddParameter("@LIEF_N_NR", LIEF_N_NR);
            DataRow? row = await fbController.SelectRowAsync(@"select OUT_ZUGDAT as LetzteLieferung,
OUT_OP as OffeneRechnungen,
OUT_BESTOFF as OffeneBestellungen,
OUT_BELASTUNG as OffeneBelastungen,
OUT_ANFRAGE as OffeneAnfragen,
OUT_RMAWERT as WertOffeneRMA,
OUT_LETZTBESTELL as LetzteBestellung ,
OUT_ZUGANG as Jahresumsatz
 from PROZ_LIEFERANTENWERTE(@LIEF_N_NR)");

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

        }

        /// <summary>
        /// Lädt sich asynchron den Jahresumsatz eines Jahres aus der Datenbank
        /// </summary>
        /// <param name="LIEF_N_NR">Die Lieferantennummer zu der sich der Jahresumsatz geladen werden soll</param>
        /// <param name="year">Das Jahr zu dem sich der Lieferantenumsatz geladen werden soll</param>
        /// <returns></returns>
        public async Task<Jahresumsatz> GetJahresUmsatzAsync(int LIEF_N_NR, int year)
        {
            using FbController2 fbController = new FbController2();
            fbController.AddParameter("@LIEF_N_NR", LIEF_N_NR);
            fbController.AddParameter("@YEAR", year);
            string sql = "SELECT SUM(ZUGA_N_NETTO + ZUGA_N_TRANSPORT) as umsatz, extract(month from ZUGA_D_RE_DATUM) as monat FROM ZUGAENGE WHERE extract(year from ZUGA_D_RE_DATUM) = @YEAR AND ZUGA_N_LIEF = @LIEF_N_NR GROUP BY monat";
            DataTable data = await fbController.SelectDataAsync(sql);
            Dictionary<int, decimal> werte = new Dictionary<int, decimal>();
            foreach (DataRow row in data.Rows)
            {
                werte.Add(row.Field<short>("monat"), row.Field<decimal>("umsatz"));
            }
            return new Jahresumsatz(werte);
        }

        public async Task<int> GetAnzahlLieferantenKontaktAsync(int LFKO_N_LFNR, DateTime from, DateTime to, string? suchbegriff = null)
        {
            using FbController2 fbController = new FbController2();
            string sql = $"SELECT COUNT(*) FROM WK5_LIEFERANTENKONTAKTE WHERE 1=1";
            if (from != DateTime.MinValue && to != DateTime.MinValue)
            {
                sql += "AND CAST(LFKO_D_DATE AS DATE) BETWEEN CAST(@FROM AS DATE) AND CAST(@TO AS DATE)";
            }
            else if (from != DateTime.MinValue && to == DateTime.MinValue)
            {
                sql += " AND CAST(LFKO_D_DATE AS DATE) > @FROM";
            }
            else if (from == DateTime.MinValue && to != DateTime.MinValue)
            {
                sql += " AND CAST(LFKO_D_DATE AS DATE) < @TO";
            }

            if (!String.IsNullOrEmpty(suchbegriff))
            {
                sql += $@" AND (CAST(LFKO_N_ID AS varchar(100)) LIKE '%{suchbegriff}%'
OR CAST(LFKO_N_LFNR AS varchar(100)) LIKE '%{suchbegriff}%'
OR CAST(LFKO_N_BESTELLNR AS varchar(100)) LIKE '%{suchbegriff}%'
OR UPPER(LFKO_A_NOTIZ) LIKE '%{suchbegriff.ToUpper()}%') ";
            }

            sql += " AND LFKO_N_LFNR = @LFKO_N_LFNR";

            fbController.AddParameter("@FROM", from.ToShortDateString());
            fbController.AddParameter("@TO", to.ToShortDateString());
            fbController.AddParameter("@LFKO_N_LFNR", LFKO_N_LFNR);

            var anzahlObj = await fbController.FetchObjectAsync(sql);

            if (anzahlObj is null)
            {
                return 0;
            }

            int.TryParse(anzahlObj.ToString(), out int anzahl);

            return anzahl;
        }

        public async IAsyncEnumerable<LieferantenKontakt> SucheLieferantenKontaktAsync(int LFKO_N_LFNR, DateTime from, DateTime to, string? suchbegriff = null, int seite = 1, int limit = 30)
        {
            using FbController2 fbController = new FbController2();
            string sql = $"SELECT FIRST {limit} SKIP {(seite - 1) * limit} * FROM WK5_LIEFERANTENKONTAKTE WHERE 1=1 ";
            if (from != DateTime.MinValue && to != DateTime.MinValue)
            {
                sql += " AND CAST(LFKO_D_DATE AS DATE) BETWEEN @FROM AND @TO";
            }
            else if (from != DateTime.MinValue && to == DateTime.MinValue)
            {
                sql += " AND CAST(LFKO_D_DATE AS DATE) > @FROM";
            }
            else if (from == DateTime.MinValue && to != DateTime.MinValue)
            {
                sql += " AND CAST(LFKO_D_DATE AS DATE) < @TO";
            }

            if (!String.IsNullOrEmpty(suchbegriff))
            {
                sql += $@" AND (CAST(LFKO_N_ID AS varchar(100)) LIKE '%{suchbegriff}%'
OR CAST(LFKO_N_LFNR AS varchar(100)) LIKE '%{suchbegriff}%'
OR CAST(LFKO_N_BESTELLNR AS varchar(100)) LIKE '%{suchbegriff}%'
OR UPPER(LFKO_A_NOTIZ) LIKE '%{suchbegriff.ToUpper()}%') ";
            }

            sql += " AND LFKO_N_LFNR = @LFKO_N_LFNR";

            sql += " ORDER BY LFKO_D_DATE DESC";

            fbController.AddParameter("@FROM", from);
            fbController.AddParameter("@TO", to);
            fbController.AddParameter("@LFKO_N_LFNR", LFKO_N_LFNR);
            var data = await fbController.SelectDataAsync(sql);

            foreach (DataRow row in data.Rows)
            {
                yield return await Task.FromResult(ObjectErweiterung.DataRowZuObjekt(new LieferantenKontakt(), row));
            }

        }

        public async IAsyncEnumerable<Bestellung?> SucheLieferantenBestellungenAsync(LieferantenBestellungenFilter filter, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            using FbController2 fbController = new FbController2();
            string sql = filter.ToSqlQuery(fbController);


            var data = await fbController.SelectDataAsync(sql);

            foreach (DataRow row in data.Rows)
            {
                if (!cancellationToken.IsCancellationRequested)
                {
                    int bestnr = row.Field<int>("BEST_N_NR");
                    if (bestnr > 0)
                    {
                        yield return await Bestellung.GetBestellungAsync(bestnr, fbController);
                    }
                }
            }
        }

        public async Task<int> GetAnzahlLieferantenBestellungenAsync(LieferantenBestellungenFilter filter)
        {
            using FbController2 fbController = new FbController2();
            string sql = filter.ToCountQuery(fbController);

            var row = await fbController.SelectRowAsync(sql);

            if (row is null)
            {
                return 0;
            }

            int.TryParse(row["COUNT"].ToString(), out int anzahl);
            return anzahl;
        }

        public async IAsyncEnumerable<Lieferantenartikel?> SucheLieferantenArtikelAsync(LieferantenArtikelFilter filter, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            using FbController2 fbController = new FbController2();
            string sql = filter.ToSqlQuery(fbController);

            var data = await fbController.SelectDataAsync(sql);

            foreach (DataRow row in data.Rows)
            {
                if (!cancellationToken.IsCancellationRequested)
                {
                    string artinr = row.Field<string>("ARLI_A_ARTIKELNR") ?? String.Empty;
                    if (!String.IsNullOrWhiteSpace(artinr))
                    {
                        yield return await Lieferantenartikel.GetLieferantenartikelAsync(artinr, filter.Lieferantennummer, fbController);
                    }
                }
            }
        }

        public async Task<int> GetAnzahlLieferantenArtikelAsync(LieferantenArtikelFilter filter)
        {
            using FbController2 fbController = new FbController2();
            string sql = filter.ToCountQuery(fbController);

            var row = await fbController.SelectRowAsync(sql);


            if (row is null)
            {
                return 0;
            }

            int.TryParse(row["COUNT"].ToString(), out int anzahl);
            return anzahl;
        }

        public async Task LöscheLieferantenArtikelAsync(int ARLI_N_LIEFNR, string ARLI_A_ARTIKELNR)
        {
            using FbController2 fbController = new FbController2();
            string sql = "DELETE FROM LIEFERANTENARTIKEL WHERE ARLI_N_LIEFNR = @ARLI_N_LIEFNR AND ARLI_A_ARTIKELNR = @ARLI_A_ARTIKELNR";
            fbController.AddParameter("@ARLI_N_LIEFNR", ARLI_N_LIEFNR);
            fbController.AddParameter("@ARLI_A_ARTIKELNR", ARLI_A_ARTIKELNR);
            await fbController.QueryAsync(sql);
        }
    }
}
