using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using WK5.Core.Models;
using WK5.Core.Services;

namespace WK5.Core
{
    public class SNSwitcher
    {
        /// <summary>
        /// Die Tabellen mit den zugehörigen Spalten in denen die Seriennr auftreten kann
        /// </summary>
        private static readonly Dictionary<string, List<string>> SNTableKeys = new Dictionary<string, List<string>>
            {
                {"BELEGSN", new List<string>{"BSNR_A_SN"} },
                {"SN", new List<string>{"SNNR_A_SN"} },
                {"SNHISTORIE", new List<string>{"SNHI_A_SN"} },
                {"WK5_BUCHUNG_SN", new List<string>{"BUCHUNGSN_A_SN"} },
                {"WK5_INVENTUR_BUCHUNG_SN", new List<string>{"IVSN_A_SN"} },
                {"ABGAENGE", new List<string>{"ARAB_A_SN"} },
                {"RMAS", new List<string>{ "RMA_A_SERIENNUMMER"} }
            };

        /// <summary>
        /// Ändert eine Seriennummer ohne weiter Prüfung in allen Seriennummer Spalten die in SNTableKeys definiert sind.
        /// Im Fehlerfall werden alle Änderungen Rückgängig gemacht.p
        /// </summary>
        /// <param name="altSN">Die alte Seriennummer die geändert werden soll</param>
        /// <param name="neuSN">Die neue Seriennummer die eingetragen werden soll</param>
        /// <returns>Gibt einen <see cref="Task"/> of <see cref="bool"/> zurück der angibt ob die Operation erfolgreich war</returns>
        public static async Task<bool> Change(string altSN, string neuSN)
        {

            using FbController2 fbController = new FbController2();
            await fbController.StartTransactionAsync();

            try
            {
                foreach (KeyValuePair<string, List<string>> kvp in SNTableKeys)
                {
                    foreach (string col in kvp.Value)
                    {
                        string sql = $"UPDATE {kvp.Key} SET {col} = @{col}_NEU WHERE {col} = @{col}_ALT";
                        fbController.AddParameter($"@{col}_NEU", neuSN);
                        fbController.AddParameter($"@{col}_ALT", altSN);
                        await fbController.QueryAsync(sql);
                    }
                }
            }
            catch (Exception ex)
            {
                await fbController.RollbackChangesAsync();
                fbController.Dispose();
                return false;
            }
            await fbController.CommitChangesAsync();
            fbController.Dispose();
            return true;


        }

        /// <summary>
        /// Ändert die Seriennummer in einem Beleg mit der zugehörigen Logik
        /// Ändert auch die Mengen der neuen und alten Seriennummern und in der BelegSeriennummer die Chargen
        /// </summary>
        /// <param name="altSN">Die alte Seriennummer</param>
        /// <param name="neuSN">Die neue Seriennummer</param>
        /// <param name="belegTyp">Der BelegTyp des Beleg in dem die Seriennummer geändert werden soll</param>
        /// <param name="belegNr">Die Belegnummer des Beleg in dem die Seriennummer geändert werden soll</param>
        /// <returns></returns>
        public static async Task<(bool success, string message)> ChangeBelegSn(string altSN, string neuSN, BelegTyp belegTyp, int belegNr, ChargenService chargenService)
        {
            using FbController2 fbController = new FbController2();

            await fbController.StartTransactionAsync();

            BelegSeriennummer? belegSn = await BelegSeriennummer.FindBelegSeriennummer(belegTyp, belegNr, altSN);
            if (belegSn is null)
            {
                return (false, "Die Belegseriennummer konnte nicht gefunden werden");
            }

            Seriennummer? neueSeriennummer = await Seriennummer.FindeOffeneSeriennummernAsync(neuSN).Where(x => !x.Ausgeliefert).FirstOrDefaultAsync();
            if (neueSeriennummer is null)
            {
                return (false, "Die neue Seriennummer konnte nicht gefunden werden");
            }

            Charge? neueCharge = await Charge.GetChargeAsync(neueSeriennummer.Charge, fbController);
            if (neueCharge is null)
            {
                return (false, "Die Charge der neuen Seriennummer konnte nicht gefunden werden");
            }

            Seriennummer? alteSeriennummer = await Seriennummer.GetSeriennummerAsync(belegSn.BSNR_A_SN!, belegSn.BSNR_N_CHARGE);
            if (alteSeriennummer is null)
            {
                return (false, "Die alte Serienummer konnte nicht gefunden werden");
            }

            Charge? alteCharge = await Charge.GetChargeAsync(alteSeriennummer.Charge, fbController);
            if (alteCharge is null)
            {
                return (false, "Die alte Charge konnte nicht gefunden werden");
            }

            try
            {


                alteCharge.CHAR_N_MENGEOFFEN++;
                await chargenService.UpdateChargeAsync(alteCharge, fbController);

                alteSeriennummer.Menge++;
                alteSeriennummer.Ausgeliefert = false;
                await Seriennummer.UpdateSeriennummerAsync(alteSeriennummer, fbController);

                neueCharge.CHAR_N_MENGEOFFEN--;
                await chargenService.UpdateChargeAsync(neueCharge, fbController);

                neueSeriennummer.Menge--;
                neueSeriennummer.Ausgeliefert = true;
                await Seriennummer.UpdateSeriennummerAsync(neueSeriennummer, fbController);

                belegSn.BSNR_A_SN = neueSeriennummer.Nummer;
                belegSn.BSNR_N_CHARGE = neueSeriennummer.Charge;

                await BelegSeriennummer.UpdateBelegSeriennummerAsync(belegSn, fbController);

                await fbController.CommitChangesAsync();
                fbController.Dispose();
                return (true, "Die Belegseriennummer wurde erfolgreich upgedatet");
            }
            catch (Exception ex)
            {
                await fbController.RollbackChangesAsync();
                fbController.Dispose();
                return (false, $"Fehler beim ändern der Seriennummern. Änderungen wurden nicht übernommen. Fehler: {ex.Message}");
            }








            /*
             Wenn wir in einem Beleg die Seriennummer ändern möchten, dann:

            müssen wir zuerst rausfinden welcher charge die neue Seriennummer angehört.
            Dann müssen wir schauen ob die neue Seriennummer schon in einem andere Beleg drin ist.
                Wenn das der Fall ist, dann brechen wir ab. (evtl. später Seriennummern tauschen?
            Wenn nicht dann
                Erhöhen wir die offene Menge der alten Seriennummern Charge um 1
                Erhöhen die Menge in der alten Seriennummer um 1 und setzen ausgeliefert auf false

             */
        }


    }
}
