using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Helpers.Export;
using Elwig.Models.Dtos;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Elwig.Windows {
    public partial class MainWindow : ContextWindow {

        public MainWindow() {
            InitializeComponent();
            var v = Assembly.GetExecutingAssembly().GetName().Version;
            VersionField.Text = "Version: " + (v == null ? "?" : $"{v.Major}.{v.Minor}.{v.Build}") + $" – {App.BranchName}";
            if (App.Client.Client == null) VersionField.Text += " (Unbekannt)";
            Menu_Help_Update.IsEnabled = App.Config.UpdateUrl != null;
            Menu_Help_Smtp.IsEnabled = App.Config.Smtp != null;
            DownloadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden;
            UploadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden;
        }

        private void Window_Loaded(object sender, RoutedEventArgs evt) {
            SeasonInput.Value = Utils.CurrentLastSeason;
        }

        private void Window_Closing(object sender, CancelEventArgs evt) {
            if (App.NumWindows > 1 && !App.ForceShutdown) {
                foreach (var w in App.Current.Windows.Cast<Window>().Where(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) {
                    try {
                        w.Close();
                    } catch { }
                }
                Thread.Sleep(100);
                if (App.NumWindows > 1 && !App.Current.Windows.Cast<Window>().Any(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) {
                    var res = MessageBox.Show("Es sind noch weitere Fenster geöffnet.\nSollen alle Fenster geschlossen werden?",
                        "Elwig beenden", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
                    if (res != MessageBoxResult.Yes) {
                        evt.Cancel = true;
                    } else {
                        Application.Current.Shutdown();
                    }
                }
            }
        }

        private async void Menu_Help_Update_Click(object sender, RoutedEventArgs evt) {
            await App.CheckForUpdates(true);
        }

        private async void Menu_Help_Smtp_Click(object sender, RoutedEventArgs evt) {
            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                using var client = await Utils.GetSmtpClient();
                await client!.DisconnectAsync(true);
                MessageBox.Show("E-Mail-Einstellungen erfolgreich überprüft!", "Erfolg", MessageBoxButton.OK, MessageBoxImage.Information);
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private void Menu_Help_Directory_Click(object sender, RoutedEventArgs evt) {
            try {
                Process.Start("explorer.exe", App.DataPath);
            } catch { }
        }

        private void Menu_Help_Config_Click(object sender, RoutedEventArgs evt) {
            try {
                Process.Start(new ProcessStartInfo {
                    FileName = "notepad.exe",
                    Arguments = App.ConfigPath,
                    Verb = "runas",
                    UseShellExecute = true,
                });
            } catch { }
        }

        private void Menu_Database_Query_Click(object sender, RoutedEventArgs evt) {
            var w = new QueryWindow();
            w.Show();
        }

        private void Menu_Database_Open_Click(object sender, RoutedEventArgs evt) {
            if (Path.GetDirectoryName(App.Config.DatabaseFile) is string path)
                Process.Start("explorer.exe", path);
        }

        private void Menu_Database_Export_Click(object sender, RoutedEventArgs evt) {
            // TODO Menu_Database_Export_Click
        }

        private async void Menu_Database_Import_Click(object sender, RoutedEventArgs evt) {
            try {
                var d = new OpenFileDialog() {
                    Title = "Export-Datei auswählen - Elwig",
                    DefaultExt = "elwig.zip",
                    Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
                    Multiselect = true,
                };
                if (d.ShowDialog() == true) {
                    Mouse.OverrideCursor = Cursors.AppStarting;
                    await ElwigData.Import(d.FileNames, ElwigData.ImportMode.Interactively);
                }
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private async void DownloadButton_Click(object sender, RoutedEventArgs evt) {
            if (App.Config.SyncUrl == null)
                return;
            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                var data = await Utils.GetExportMetaData(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
                var files = data
                    .Select(f => new {
                        Name = f!["name"]!.AsValue().GetValue<string>(),
                        Timestamp = f!["timestamp"] != null && DateTime.TryParseExact(f!["timestamp"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null,
                        ZwstId = f!["meta"]?["zwstid"]?.AsValue().GetValue<string>() ?? f!["zwstid"]?.AsValue().GetValue<string>(),
                        Device = f!["meta"]?["device"]!.AsValue().GetValue<string>(),
                        Url = f!["url"]!.AsValue().GetValue<string>(),
                        Size = f!["size"]!.AsValue().GetValue<long>(),
                    })
                    .Where(f => f.Timestamp >= new DateTime(Utils.CurrentLastSeason, 7, 1))
                    .ToList();

                var imported = await ElwigData.GetImportedFiles();
                var import = files
                    .Where(f => f.Device != Environment.MachineName && !imported.Contains(f.Name))
                    .ToList();
                var paths = new List<string>();
                using (var client = Utils.GetHttpClient(App.Config.SyncUsername, App.Config.SyncPassword)) {
                    foreach (var f in import) {
                        var filename = Path.Combine(App.TempPath, f.Name);
                        using var stream = new FileStream(filename, FileMode.Create);
                        await client.DownloadAsync(f.Url, stream);
                        paths.Add(filename);
                    }
                }
                await ElwigData.Import(paths, ElwigData.ImportMode.FromBranches);
            } catch (HttpRequestException exc) {
                MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            } catch (TaskCanceledException exc) {
                MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private async void UploadButton_Click(object sender, RoutedEventArgs evt) {
            if (App.Config.SyncUrl == null)
                return;
            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                var path = Path.Combine(App.TempPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip");
                using var ctx = new AppDbContext();
                var deliveries = await ctx.Deliveries
                    .Where(d => d.Year == Utils.CurrentLastSeason && d.ZwstId == App.ZwstId)
                    .Include(d => d.Parts)
                    .ThenInclude(p => p.PartModifiers)
                    .OrderBy(d => d.DateString)
                    .ThenBy(d => d.TimeString)
                    .ThenBy(d => d.LsNr)
                    .AsSplitQuery()
                    .ToListAsync();
                if (deliveries.Count == 0) {
                    MessageBox.Show("Es gibt keine Lieferungen, die hochgeladen werden können!", "Lieferungen hochladen",
                        MessageBoxButton.OK, MessageBoxImage.Error);
                } else {
                    await ElwigData.Export(path, deliveries, [$"{Utils.CurrentLastSeason}", $"Zweigstelle {App.BranchName}"]);
                    await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
                    MessageBox.Show($"Hochladen von {deliveries.Count} Lieferungen erfolgreich!", "Lieferungen hochladen",
                        MessageBoxButton.OK, MessageBoxImage.Information);
                }
            } catch (HttpRequestException exc) {
                MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
            } catch (TaskCanceledException exc) {
                MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private void MemberAdminButton_Click(object sender, RoutedEventArgs evt) {
            var w = new MemberAdminWindow();
            w.Show();
        }

        private void ReceiptButton_Click(object sender, RoutedEventArgs evt) {
            App.FocusReceipt();
        }

        private void DeliveryAdminButton_Click(object sender, RoutedEventArgs evt) {
            var w = new DeliveryAdminWindow();
            w.Show();
        }

        private void BaseDataButton_Click(object sender, RoutedEventArgs evt) {
            App.FocusBaseData();
        }

        private void MailButton_Click(object sender, RoutedEventArgs evt) {
            App.FocusMailWindow();
        }

        protected override Task OnRenewContext(AppDbContext ctx) {
            SeasonInput_TextChanged(null, null);
            return Task.CompletedTask;
        }

        private void SeasonFinish_Expanded(object sender, RoutedEventArgs evt) {
            Height = 530;
        }

        private void SeasonFinish_Collapsed(object sender, RoutedEventArgs evt) {
            Height = 390;
        }

        private async void SeasonInput_TextChanged(object? sender, TextChangedEventArgs? evt) {
            using var ctx = new AppDbContext();
            var s0 = await ctx.Seasons.FindAsync(SeasonInput.Value);
            var valid = (s0 != null);
            DeliveryConfirmationButton.IsEnabled = valid;
            OverUnderDeliveryButton.IsEnabled = valid;
            PaymentButton.IsEnabled = valid;
            BreakdownButton.IsEnabled = valid;
        }

        private void DeliveryConfirmationButton_Click(object sender, RoutedEventArgs evt) {
            if (SeasonInput.Value is not int year)
                return;
            var w = App.FocusMailWindow(year);
            w.AddDeliveryConfirmation();
        }

        private async void OverUnderDeliveryButton_Click(object sender, RoutedEventArgs evt) {
            if (SeasonInput.Value is not int year)
                return;
            var d = new SaveFileDialog() {
                FileName = $"Über-Unterlieferungen-{year}.ods",
                DefaultExt = "ods",
                Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
                Title = $"Über-/Unterlieferungen {year} speichern unter - Elwig"
            };
            if (d.ShowDialog() == false)
                return;

            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                var b = new Billing(year);
                await b.FinishSeason();
                await b.CalculateBuckets();
                await App.HintContextChange();

                using var ctx = new AppDbContext();
                var tbl1 = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, year);
                var tbl2 = await AreaComUnderDeliveryData.ForSeason(ctx.AreaComUnderDeliveryRows, year);
                var tbl3 = await MemberDeliveryPerVariantData.ForSeason(ctx.MemberDeliveryPerVariantRows, year);
                using var ods = new OdsFile(d.FileName);
                await ods.AddTable(tbl1);
                await ods.AddTable(tbl2);
                await ods.AddTable(tbl3);
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private void PaymentButton_Click(object sender, RoutedEventArgs evt) {
            if (SeasonInput.Value is not int year)
                return;
            App.FocusPaymentVariants(year);
        }

        private async void BreakdownButton_Click(object sender, RoutedEventArgs evt) {
            if (SeasonInput.Value is not int year)
                return;
            var d = new SaveFileDialog() {
                FileName = $"Aufschlüsselung-{year}.ods",
                DefaultExt = "ods",
                Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
                Title = $"Sorten-/Qualitätsaufschlüsselung {year} speichern unter - Elwig"
            };
            if (d.ShowDialog() == false)
                return;

            Mouse.OverrideCursor = Cursors.AppStarting;
            try {
                var b = new Billing(year);
                await b.FinishSeason();
                await b.CalculateBuckets();
                await App.HintContextChange();

                using var ctx = new AppDbContext();
                using var ods = new OdsFile(d.FileName);
                var tblTotal = await WeightBreakdownData.ForSeason(ctx.WeightBreakDownRows, year);
                await ods.AddTable(tblTotal);
                foreach (var branch in await ctx.Branches.OrderBy(b => b.Name).ToListAsync()) {
                    var tbl = await WeightBreakdownData.ForSeason(ctx.WeightBreakDownRows, year, branch);
                    await ods.AddTable(tbl);
                }
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }
    }
}