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

namespace DatenbankMigration
{
    internal class Program
    {
        // ACHTUNG: Es gibt auch Tabellen die unabhängig von der ID immer eine neue ID generieren, das vor dem nächsten Import einmal prüfen
        private static async Task Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                            .MinimumLevel.Debug()
                            .WriteTo.File("Migration.log")
                            .WriteTo.Console()
                            .CreateLogger();
            using FbController2 fbControllerLive = new FbController2(@"initial catalog=W4IMPORT;password=masterkey;user id=SYSDBA;data source=srv01");
            using FbController2 fbControllerLocal = new FbController2(@"initial catalog=W4;password=masterkey;user id=SYSDBA;data source=localhost");

            List<string> insertTables = new List<string>()
            {
                "ABGAENGE",
                "BANKEN",
                "WAEHRUNG",
                "LANDESKENNZEICHEN",
                "LAGER",
                "EXCEPTIONS",
                "LIEFERBEDINGUNG",
                "ZAHLUNGSBEDINGUNG",
                "MEHRWERTSTEUER",
                "MASSEINHEITEN",
                "NUMKREISE",
                "OPTIONEN",
                "MELDUNGEN",
                "PLZ",
                "PREISGRUPPE",
                "PREISLISTE",
                "RABATTBEZEICHNUNGEN",
                "KON1_VOREINSTELL",
                "KONF_FIRMA",
                "ERLOESKONTO",
                "KOSTENSTELLE",
                "KUNDENGRUPPE",
                "SELEKTIONSMERKMAL",
                "SPRACHE",
                "WARENGRUPPE",
                "UNTERWARENGRUPPE",
                "PERSONAL",
                "VERTRETER",
                "ZOLLTARIF",
                "ANREDEN",
                "ARTIKEL",
                "ARTIKELWERTE",
                "ARTIKELSTUECKLISTE",
                "ARTIKELBEWEGUNG",
                "ARTIKELZUBEHOER",
                "KUNDEN",
                "ANSPRECHPARTNER",
                "KUNDENKONTAKTE",
                "KUNDENRABATTE",
                "KUNDENRECHNANSCHR",
                "KUNDENSACHNUMMER",
                "LIEFERANSCHRIFTEN",
                "LIEFERANTEN",
                "LIEFERANTENARTIKEL",
                "LIEFERZEIT_TEXTE",
                "LIEFRECHNANSCHR",
                "LIEF_PARTNER",
                "CHARGEN",
                "SN",
                "SNHISTORIE",
                "ZUGAENGE",
                "ZUGANG_ZUSATZKOST",
                "ZUSATZKOSTEN",
                "HERSTELLER",
                "BELEGTEXTE",
                "BELEGSTATUS",
                "BELEGE",
                "BELEGCHARGEN",
                "BELEGPOS",
                "BELEGSTUECKLISTE",
                "BELEGSN",
                "BELEG_CHANGE",
                "BELEG_STAT_BUCHUNG",
                "BESTELLUNGEN",
                "BESTELLUNGEN_POS",
                "WK5_ARTIKELBILDER",
                "WK5_ARTIKELDOKUMENTE",
                "WK5_ARTIKELNUMMER_REPLACE",
                "WK5_ARTIKEL_ANGEBOTE",
                "WK5_AUFTRAGSFREIGABE",
                //"WK5_AUSSENGEBIETE",
                "WK5_BUCHUNG",
                "WK5_BUCHUNG_SN",
                "WK5_CALL_LOG",
                "WK5_CONFIG",
                "WK5_EMAIL_LOGS",
                "WK5_GEPACKT",
                "WK5_GRUPPEN",
                "WK5_HAUPTLAGER",
                "WK5_INVENTUR",
                "WK5_INVENTUR_BUCHUNG",
                "WK5_INVENTUR_BUCHUNG_KOMMENTARE",
                "WK5_INVENTUR_BUCHUNG_SN",
                "WK5_INVENTUR_REGAL_GESCHLOSSEN",
                "WK5_KUNDEN_ZUSATZ",
                "WK5_LIEFERANTENKONTAKTE",
                "WK5_MANUELL_VERSAND",
                "WK5_NOTIFICATIONS",
                "WK5_PERSONAL_ZU_GRUPPE",
                "WK5_SELEKTIONSSTATISTIK",
                "WK5_STORNIEREN_TEXTE",
                "WK5_TEXTE",
                "WK5_TODO",
                "WK5_VERSAND_GEWICHTE",
                "WK5_WDH_RECHNUNGEN",
                "WK5_WDH_RECHNUNGEN_CHANGE",
                "WK5_WERBEMITTEL",
                "WK5_WERBEMITTEL_ZU_KUNDE",
                "WK5_WERBEMITTEL_ZU_UGRUPPE",
                "WK5_WICHTIGKEIT_ARTIKEL",
                "WK5_WICHTIGKEIT_KUNDEN",
                "WK5_WICHTIGKEIT_LIEFERANTEN",
                "WK5_ZONEN",
                "WK5_ZONE_ZU_LAND",
                "PROVISION",
                "PAKETE",
                "ZAHLUNGEN"
            };




            List<(string name, long value)> sequences = (await GetSequencesAsync(fbControllerLive).ToListAsync()).OrderBy(x => x.name).ToList();

            Log.Logger.Information("Anpassung der Sequences");
            foreach (var item in sequences)
            {
                if (item.name != "GEN_ARBE_N_ID" && item.name != "GEN_BEL_CHANGE")
                {
                    Log.Logger.Information($"Setzte SEQUENCE {item.name} auf {item.value}");
                    await fbControllerLocal.QueryAsync($"ALTER SEQUENCE {item.name} RESTART WITH {item.value}");
                }
            }

            Log.Logger.Information("-----------------------");
            Log.Logger.Information("");
            Log.Logger.Information("");
            Log.Logger.Information("-----------------------");
            Log.Logger.Information($"Update {insertTables.Count} Tabellen");
            // Es gibt Tabellen, die automatisch durch einen Trigger befüllt werden, diese müssen vorher einmal nochmal geleert werden
            List<string> requireDeleteAgain = new List<string>
            {
                "ARTIKELWERTE",
                "BELEGSTUECKLISTE"
            };
            for (int i = insertTables.Count - 1; i > -1; i--)
            {
                Log.Logger.Information($"Lösche lokale Daten von {insertTables[i]}");
                await fbControllerLocal.QueryAsync($"DELETE FROM {insertTables[i]}");
            }


            Log.Logger.Information("");
            foreach (var tableName in insertTables)
            {
                Log.Logger.Information("-----------------------");
                if (requireDeleteAgain.Contains(tableName))
                {
                    Log.Logger.Information($"Tabelle {tableName} benötigt eine zusätzliche bereinigung. Lösche Datensätze...");
                    await fbControllerLocal.QueryAsync($"DELETE FROM {tableName}");
                }
                Log.Logger.Information($"Lade Columns für {tableName}");
                var tmp = await GetColumnNames(tableName, fbControllerLocal);
                int zähler = 1500;
                int page = 1;
                bool hasMore = true;
                do
                {
                    Log.Logger.Information($"Insert Daten Table:{tableName}; Page: {page}");
                    var data = await fbControllerLive.SelectDataAsync($"SELECT FIRST {zähler} SKIP ({(page - 1) * zähler}) * FROM {tableName}");

                    foreach (DataRow row in data.Rows)
                    {
                        string sql = $"INSERT INTO {tableName} ({String.Join(",", tmp)}) VALUES (@{String.Join(",@", tmp)})";

                        foreach (var columnName in tmp)
                        {
                            fbControllerLocal.AddParameter($"@{columnName}", row[columnName]);
                        }

                        await fbControllerLocal.QueryAsync(sql);
                    }

                    hasMore = data.Rows.Count is 1500;
                    page++;
                }
                while (hasMore);

                Log.Logger.Information($"Tabelle {tableName} erfolgreich importiert");
                Log.Logger.Information("-----------------------");
                Log.Logger.Information("");
            }

        }

        private static async IAsyncEnumerable<(string name, long value)> GetSequencesAsync(FbController2 fbController)
        {
            DataTable data = await fbController.SelectDataAsync(@"

execute block
returns (
    out_name char(31),
    out_value bigint)
as
begin
    for select rdb$generator_name from rdb$generators where rdb$system_flag is distinct from 1 into out_name do
    begin
        execute statement 'select gen_id(' || out_name || ', 0) from rdb$database' into out_value;
        suspend;
    end
end");

            foreach (DataRow row in data.Rows)
            {
                if (long.TryParse(row["OUT_VALUE"].ToString(), out long value))
                {
                    yield return (row.Field<string>("OUT_NAME").Trim(), value);
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }
        }

        private static async Task<List<string>> GetColumnNames(string tableName, FbController2 fbController)
        {
            fbController.AddParameter("@TABLENAME", tableName);
            DataTable data = await fbController.SelectDataAsync(@"select rdb$field_name from rdb$relation_fields
where rdb$relation_name = @TABLENAME;");

            List<string> columNames = new List<string>();
            foreach (DataRow row in data.Rows)
            {
                columNames.Add(row[0].ToString().Trim());
            }

            return columNames;
        }
    }


}
