using Serilog;
using Serilog.Core;
using System;
using System.Collections.Generic;
using Serilog.Sinks.Firebird;
using System.Data;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using WK5.Core;
using WK5.Core.Models;
using WK5.Core.OpenCart.Karley;
//#define wdhRechnungenAnKarley // Gibt an, das neu erstellte wiederkehrende Rechnungen an info@ geschickt werden, anstatt zum Kunden
namespace KarleyUpdate
{
    public class OpencartBild
    {
        public Logger logger { get; set; } = new LoggerConfiguration()
                .MinimumLevel.Information()
                .WriteTo.Console()
                .WriteTo.File(GlobalConfig.AuftragsFreigabeLogFile, rollingInterval: RollingInterval.Day)
#if DEBUG
                .WriteTo.Firebird(GlobalConfig.W4LocalDebugConnectionString, prefix: () => "KarleyUpdate OpencartBildSynchronisation ")
#else
                .WriteTo.Firebird(GlobalConfig.W4ConnectionString, prefix: () => "KarleyUpdate Auftragsfreigabe ")
#endif
                .CreateLogger();

        public async Task DuplikatBereinigiung()
        {
            //Duplikate holen
            string duplikatSql = @"SELECT ARBI_A_IMGPFAD, ARBI_A_ARTINR, COUNT(*) AS AMOUNT
FROM WK5_ARTIKELBILDER
GROUP BY ARBI_A_IMGPFAD, ARBI_A_ARTINR
HAVING COUNT(*) > 1
";

            using FbController2 fbController = new FbController2();

            DataTable data = await fbController.SelectDataAsync(duplikatSql);

            foreach (DataRow row in data.Rows)
            {
                string? img = row.Field<string>("ARBI_A_IMGPFAD");
                string? arti = row.Field<string>("ARBI_A_ARTINR");
                long count = row.Field<long>("AMOUNT");

                if (!String.IsNullOrWhiteSpace(img) && !String.IsNullOrWhiteSpace(arti))
                {
                    string idSql = @"SELECT ARBI_N_NR FROM WK5_ARTIKELBILDER WHERE ARBI_A_IMGPFAD = @IMG AND ARBI_A_ARTINR = @ARTI";

                    fbController.AddParameter("@IMG", img);
                    fbController.AddParameter("@ARTI", arti);

                    DataTable idData = await fbController.SelectDataAsync(idSql);

                    List<int> ids = idData.Rows.Cast<DataRow>().Select(x => x.Field<int>("ARBI_N_NR")).ToList();

                    List<int> deletedIds = ids.Skip(1).ToList();

                    int id = ids.First();

                    string deleteSql = @$"DELETE FROM WK5_ARTIKELBILDER WHERE ARBI_N_NR IN ({String.Join(',', deletedIds)})";

                    await fbController.QueryAsync(deleteSql);

                    //Belegpos Patchen

                    string patchSql = $"UPDATE BELEGPOS SET WK5_BPOS_N_BILD = @ID WHERE WK5_BPOS_N_BILD IN ({String.Join(',', deletedIds)})";
                    fbController.AddParameter("@ID", id);

                    await fbController.QueryAsync(patchSql);

                }

            }
        }

        public async Task DatenbankBereinigen()
        {
            //Auf ein einheitliches Pfad Schema einstellen
            await foreach (ArtikelBild bild in ArtikelBild.GetAlleBilderAsync())
            {
                string formatted = ArtikelBild.FormatPath(bild.ARBI_A_IMGPFAD);
                if (!bild.ARBI_A_IMGPFAD.Equals(formatted))
                {
                    bild.ARBI_A_IMGPFAD = formatted;
                    await ArtikelBild.UpdateAsync(bild);
                }
            }

            await DuplikatBereinigiung();

        }

        public async Task RunAsync()
        {
            logger.Information("Passe vorhandene Datenbank Bild Pfade an");

            await DatenbankBereinigen();


            logger.Information("Starte Opencart Bildsynchronisation");
            using FbController2 fbController = new FbController2();
            DataTable data = await fbController.SelectDataAsync("SELECT ARTI_A_NR FROM ARTIKEL WHERE COALESCE(ARTI_A_NR,'') != ''");

            HashSet<string> referencedArtikelBilder = new HashSet<string>();
            int count = 0;
            foreach (DataRow row in data.Rows)
            {
                Console.Write($"\r{count}/{data.Rows.Count}");
                count++;
                string? artikelnummer = row.Field<string>("ARTI_A_NR");

                if (String.IsNullOrWhiteSpace(artikelnummer))
                {
                    continue;
                }

#if DEBUG
                if (referencedArtikelBilder.Count > 100)
                {
                    break;
                }
                //if (!artikelnummer.Equals("074542DTM", StringComparison.OrdinalIgnoreCase))
                //{
                //    continue;
                //}
#endif
                // Zuerst bereinigen wir die Tabelle, damit ggf. Bilder erneut heruntergeladen werden.
                await foreach (ArtikelBild bild in ArtikelBild.GetBilderAsync(artikelnummer))
                {
                    if (!File.Exists(bild.DriveImage))
                    {
                        await ArtikelBild.DeleteAsync(bild);
                    }
                }

                //Jetzt laden wir die Bilder von Opencart, fügen Sie in die ArtikelBilder Tabelle ein und laden Sie herunter wenn der Eintrag neu angelegt wurde
                List<KarleyProductImage> opencartImages = await KarleyProductImage.GetImagesAsync(artikelnummer, GlobalConfig.KarleyMySql).ToListAsync();
                foreach (KarleyProductImage bild in opencartImages)
                {
                    if (!String.IsNullOrWhiteSpace(bild.Image))
                    {
                        bild.Image = ArtikelBild.FormatPath(bild.Image);
                        string sql = @"UPDATE OR INSERT INTO WK5_ARTIKELBILDER (ARBI_A_ARTINR, ARBI_A_IMGPFAD)
VALUES(@ARBI_A_ARTINR, @ARBI_A_IMGPFAD)
MATCHING(ARBI_A_ARTINR, ARBI_A_IMGPFAD)
RETURNING OLD.ARBI_N_NR AS OLD_ID, NEW.ARBI_N_NR AS NEW_ID";

                        fbController.AddParameter("@ARBI_A_ARTINR", artikelnummer);
                        fbController.AddParameter("@ARBI_A_IMGPFAD", bild.Image);
                        DataTable res = await fbController.SelectDataAsync(sql);

                        DataRow resRow = res.Rows[0];

                        int old_id = (int)(resRow.Field<int?>("OLD_ID") ?? 0);
                        int new_id = (int)(resRow.Field<int?>("NEW_ID") ?? 0);

                        // Wir brauchen das Bild an sich nur herunterzuladen, wenn es neu ist (old_id = 0 || old_id != new_id)
                        if (old_id != new_id)
                        {
                            string file = Path.Combine(ArtikelBild.LocalImageDirectory, bild.Image);
                            string? path = await bild.Download(file);
                        }
                    }
                }

                // Jetzt laden wir alle Bilder die wir kennen in einen HashTable
                await foreach (ArtikelBild bild in ArtikelBild.GetBilderAsync(artikelnummer))
                {
                    if (File.Exists(bild.DriveImage))
                    {
                        referencedArtikelBilder.Add(bild.DriveImage);
                    }
                }
            }

            // Hier können wir unseren HashTable durchgehen und alle Bilder entfernen, die auf Dateiebene existieren aber nicht referenziert sind.
            logger.Information("Starte Opencart Bilder Cleanup");
            HashSet<string> allFiles = Directory.GetFiles(ArtikelBild.LocalImageDirectory, "*.*", SearchOption.AllDirectories).Distinct().Select(x => Path.GetFullPath(ArtikelBild.FormatPath(x))).ToHashSet();
            allFiles.ExceptWith(referencedArtikelBilder);

            foreach (string file in allFiles)
            {
                logger.Information($"Lösche {file} (nicht in ArtikelBilder referenziert)");
                File.Delete(file);
            }

            logger.Information($"Leere Verzeichnisse werden gelöscht...");
            DeleteEmptyDirectories(ArtikelBild.LocalImageDirectory);
        }

        private void DeleteEmptyDirectories(string root)
        {
            try
            {
                foreach (string d in Directory.EnumerateDirectories(root))
                {
                    DeleteEmptyDirectoriesRecursed(d);
                }
            }
            catch (Exception) { }
        }

        private void DeleteEmptyDirectoriesRecursed(string dir)
        {
            try
            {
                foreach (string d in Directory.EnumerateDirectories(dir))
                {
                    DeleteEmptyDirectoriesRecursed(d);
                }


                IEnumerable<string> entries = Directory.EnumerateFileSystemEntries(dir);
                if (!entries.Any())
                {
                    try
                    {
                        logger.Information($"Lösche {dir} (keine Einträge)");
                        Directory.Delete(dir);
                    }
                    catch (Exception) { }
                }

            }
            catch (Exception) { }
        }
    }
}