using System;
using System.Collections.Generic;
using global::System.IO;
using System.Linq;
using System.Windows.Forms;
using Microsoft.VisualBasic.CompilerServices;
using Z.Collections.Extensions;
using System.Threading.Tasks;
using Pilz.Updating.Administration.Discord;
using Telerik.WinControls.UI;
using Telerik.WinControls;
using Pilz.Updating.Administration.Packaging;

namespace Pilz.Updating.Administration.GUI
{
    public partial class UpdateManagerWindow
    {

        // C o n s t a n t s

        private const string FILTER_UPDATEINFO_CONFIGURATION = "JSON (*.json)|*.json";
        private const string FILTER_UPDATEPROJECT = "Update-Info-Konfiguration (*.udic)|*.udic";
        private const string FILTER_UPDATEPACKAGE = "ZIP-Archiv (*.zip)|*.zip";
        private const string FILTER_PACKAGE_TEMPLATE = "Update-Paket-Vorlagen (*.udpt)|*.udpt";
        private const string FILTER_PACKAGE_ZIP_PACKAGE = "ZIP-Paket (*.zip)|*.zip";
        private const string FILTER_PACKAGE_ADDON = "Anwendungserweiterung (*.dll)|*.dll";

        // F i e l d s

        private string curProjectFilePath;
        private UpdateServerManager manager = null;
        private DiscordBot discordBot = null;
        private readonly UpdatePackageManager packageManager = new UpdatePackageManager();
        private string curPackageTemplatePath = string.Empty;

        // P r o p e r t i e s

        public string TempPackageFilePath { get; private set; } = string.Empty;

        // C o n s t r u c t o r s

        public UpdateManagerWindow()
        {
            this.Shown += UpdateManagerWindow_Shown;
            this.Load += UpdateManagerWindow_Load;
            this.FormClosing += UpdateManagerWindow_FormClosing;
            InitializeComponent();
            this.AllowAero = false;
            SetEnabledUiControls(false);
        }

        // F e a t u r e s

        private void ProgressControls(bool enabled)
        {
            if (enabled)
                radWaitingBar_PackageLoading.StartWaiting();
            else
                radWaitingBar_PackageLoading.StopWaiting();
        }

        private void ProgressPackagingControls(bool enabled)
        {
            if (enabled)
                radWaitingBar_PackageCreation.StartWaiting();
            else
                radWaitingBar_PackageCreation.StopWaiting();
        }

        private void SetEnabledUiControls(bool enabled, bool setProjectOptionsAlwayToTrue = false)
        {
            radRibbonBarGroup_Options.Enabled = enabled || setProjectOptionsAlwayToTrue;
            radButtonElement_SaveProject.Enabled = enabled || setProjectOptionsAlwayToTrue;
            radRibbonBarGroup_Configuration.Enabled = enabled;
            radRibbonBarGroup_NewPackage.Enabled = enabled;
        }

        private async Task CreateNewProject(string filePath)
        {
            var oldProject = General.CurProject;
            General.CurProject = new UpdateProject();
            if (My.MyProject.Forms.UpdateServerInfoEditor.ShowDialog(this) == DialogResult.OK)
            {
                curProjectFilePath = filePath;
                SaveProject(curProjectFilePath);
                await LoadManager();
            }
            else
                General.CurProject = oldProject;
        }

        private async Task OpenProject(string filePath)
        {
            curProjectFilePath = filePath;
            General.CurProject = UpdateProject.Load(filePath);
            General.SetProxyConfig();
            await LoadManager();
        }

        private void SaveProject(string filePath)
        {
            General.CurProject.Save(filePath);
        }

        private async Task LoadManager()
        {
            bool hasError;
            ProgressControls(true);

            try
            {
                manager = new UpdateServerManager(General.CurProject.UpdateServerConfig);

                if (await manager.LoadInfoFromServer())
                {
                    await LoadPackageList();
                    LoadUpdateInstallerInfos();

                    hasError = false;
                }
                else
                    hasError = true;
            }
            catch (Exception)
            {
                hasError = true;
            }

            if (hasError)
            {
                RadMessageBox.Show(this, "Ein Fehler ist aufgetreten beim laden des Servers.", string.Empty, MessageBoxButtons.OK, RadMessageIcon.Error);
                SetEnabledUiControls(false, true);
            }
            else
                SetEnabledUiControls(true);

            ProgressControls(false);
        }

        private async Task LoadPackageList()
        {
            ProgressControls(true);
            radListView_Packages.BeginUpdate();
            radListView_Packages.Items.Clear();

            foreach (var pkgVersion in await manager.GetUpdatePackagesList())
            {
                var name = manager.GetPackageDescription(pkgVersion).name;
                var cells = new List<string>();

                cells.Add(string.IsNullOrEmpty(name) ? "<Kein Titel>" : name);
                cells.Add(pkgVersion.Version.ToString());
                cells.Add(pkgVersion.Channel.ToString());
                cells.Add(pkgVersion.Build.ToString());
                cells.Add("Ja");

                var item = new ListViewDataItem(String.Empty, cells.ToArray())
                {
                    Tag = pkgVersion
                };

                radListView_Packages.Items.Add(item);
            }

            radListView_Packages.EndUpdate();
            ProgressControls(false);

            if (radListView_Packages.HasChildren)
                radListView_Packages.SelectedItem = radListView_Packages.Items[0];
        }

        private void LoadUpdateInstallerInfos()
        {
            radTextBoxControl_DownloadURL.Text = manager.UpdateInfo.UpdateInstallerLink;
        }

        private ApplicationVersion GetSelectedPackageVersion()
        {
            return radListView_Packages.SelectedItem?.Tag as ApplicationVersion;
        }

        private async Task<bool> UploadPackage(string filePath)
        {
            var success = false;
            var resVersion = EditApplicationVersion();

            if (!resVersion.canceled)
            {
                ProgressControls(true);
                if (await manager.UploadPackage(filePath, resVersion.newVersion))
                    success = true;
                ProgressControls(false);
            }

            return success;
        }

        private (ApplicationVersion newVersion, bool canceled) EditApplicationVersion()
        {
            return EditApplicationVersion(new ApplicationVersion());
        }

        private (ApplicationVersion newVersion, bool canceled) EditApplicationVersion(ApplicationVersion inputVersion)
        {
            var frm = new ApplicationVersionInput()
            {
                Version = inputVersion.Version,
                Channel = inputVersion.Channel,
                Build = inputVersion.Build
            };

            if (frm.ShowDialog(this) == DialogResult.OK)
                return (new ApplicationVersion(frm.Version, frm.Build, frm.Channel), false);
            else
                return (inputVersion, true);
        }

        private async Task<bool> DeletePackage(ApplicationVersion version)
        {
            ProgressControls(true);
            bool success = await manager.DeletePackage(version);
            ProgressControls(false);
            return success;
        }

        private async Task<bool> SaveInfoToServer()
        {
            ProgressControls(true);
            bool success = await manager.SaveInfoToServer();
            ProgressControls(false);
            return success;
        }

        private async Task<bool> ChangePackageVersion(ApplicationVersion version)
        {
            bool success = false;
            var (newVersion, canceled) = EditApplicationVersion(version);

            if (!canceled)
            {
                ProgressControls(true);
                success = await manager.ChangePackageVersion(version, newVersion);
                ProgressControls(false);
            }
            
            return success;
        }

        // G u i

        private void UpdateManagerWindow_Shown(object sender, EventArgs e)
        {
        }

        private async void UpdateManagerWindow_Load(object sender, EventArgs e)
        {
            string[] args = Environment.GetCommandLineArgs();

            if (args.Length > 1)
            {
                try
                {
                    await OpenProject(args[1]);
                }
                catch (Exception) { }
            }
        }

        private void UpdateManagerWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (discordBot is object)
                discordBot.Stop();
        }

        private async void ButtonItem_NewProject_Click(object sender, EventArgs e)
        {
            var sfd_updateadministration_upa = new RadSaveFileDialog()
            {
                Filter = "Update Project Files (*.upa)|*.upa"
            };
            if (sfd_updateadministration_upa.ShowDialog(this) == DialogResult.OK)
            {
                await CreateNewProject(sfd_updateadministration_upa.FileName);
            }
        }

        private async void ButtonItem_OpenProject_Click(object sender, EventArgs e)
        {
            var ofd_updateadministration_upa = new RadOpenFileDialog()
            {
                Filter = "Update Project Files (*.upa)|*.upa"
            };
            if (ofd_updateadministration_upa.ShowDialog(this) == DialogResult.OK)
            {
                await OpenProject(ofd_updateadministration_upa.FileName);
            }
        }

        private void ButtonItem_SaveProject_Click(object sender, EventArgs e)
        {
            SaveProject(curProjectFilePath);
        }

        private async void ButtonItem_ProjectOptions_Click(object sender, EventArgs e)
        {
            My.MyProject.Forms.UpdateServerInfoEditor.ShowDialog(this);
            await LoadManager();
        }

        private async void ButtonItem_UploadUpdateConfiguration_Click(object sender, EventArgs e)
        {
            await SaveInfoToServer();
        }

        private async void ButtonItem_ExportUpdateConfiguration_Click(object sender, EventArgs e)
        {
            var sfd_UpdateAdministration_UpdateConfiguration = new RadSaveFileDialog()
            {
                Filter = FILTER_UPDATEINFO_CONFIGURATION
            };
            if (sfd_UpdateAdministration_UpdateConfiguration.ShowDialog(this) == DialogResult.OK)
                await manager.SaveInfoToFile(sfd_UpdateAdministration_UpdateConfiguration.FileName);
        }

        private async void ButtonItem_CreateAndUploadPackage_Click(object sender, EventArgs e)
        {
            var success = false;

            if (await ExportTempUpdatePackage())
            {
                if (await UploadPackage(TempPackageFilePath))
                {
                    await LoadPackageList();
                    success = true;
                }
            }

            if (!success)
                RadMessageBox.Show(this, My.Resources.UpdatingAdministrationLangRes.MsgBox_PkgExportSuccess, My.Resources.UpdatingAdministrationLangRes.MsgBox_PkgExportSuccess_Titel, MessageBoxButtons.OK, RadMessageIcon.Info);
        }

        private async void ButtonItem_UploadExistingPackage_Click(object sender, EventArgs e)
        {
            var ofd_UpdateAdministration_UpdatePackage = new RadOpenFileDialog()
            {
                Filter = FILTER_UPDATEPACKAGE
            };

            if (ofd_UpdateAdministration_UpdatePackage.ShowDialog(this) == DialogResult.OK)
            {
                if(await UploadPackage(ofd_UpdateAdministration_UpdatePackage.FileName))
                    await LoadPackageList();
            }
        }

        private async void ButtonItem_RemovePackage_Click(object sender, EventArgs e)
        {
            var version = GetSelectedPackageVersion();
            if (await DeletePackage(version))
                await LoadPackageList();
        }

        private void TextBoxX_UpdateInstallerDownloadUrl_TextChanged(object sender, EventArgs e)
        {
            manager.UpdateInfo.UpdateInstallerLink = radTextBoxControl_DownloadURL.Text.Trim();
        }

        private void ButtonItem_PostMsgInDiscord_Click(object sender, EventArgs e)
        {
            if (discordBot == null)
                LoadDiscordBot();

            if (discordBot is object)
            {
                var version = GetSelectedPackageVersion();
                var pkg = manager.GetUpdatePackageInfo(version);
                var frm = new DiscordPostDialog(discordBot, pkg);
                frm.ShowDialog(this);
            }
            else
                RadMessageBox.Show(this, "Offenbar ist ein Fehler ist aufgetreten beim Laden des Discord-Bots.", string.Empty, MessageBoxButtons.OK, RadMessageIcon.Error);
        }

        private void LoadDiscordBot()
        {
            if (discordBot is object)
                discordBot.Stop();

            discordBot = new DiscordBot(General.CurProject.DiscordBotConfig);

            bool hasLoaded = false;
            bool hasError = false;

            discordBot.GotReady += (sender, e) => hasLoaded = true;
            discordBot.LoggedMsg += (sender, msg, isError) => 
            {
                if (isError)
                    hasError = true;
            };

            discordBot.Start();

            ProgressControls(true);
            while (!hasLoaded && !hasError)
                Application.DoEvents();
            ProgressControls(false);

            if (hasError)
                discordBot = null;
        }

        private async void ButtonItem_ChangeVersion_Click(object sender, EventArgs e)
        {
            var version = GetSelectedPackageVersion();
            if (await ChangePackageVersion(version))
                await LoadPackageList();
        }

        private void ButtonItem_EditDescription_Click(object sender, EventArgs e)
        {
            var version = GetSelectedPackageVersion();
            var desc = manager.GetPackageDescription(version);

            var frm = new PackageDescriptionEditor()
            {
                Titel = desc.name,
                Description = desc.description,
                DescriptionType = desc.descriptionType
            };

            if (frm.ShowDialog(this) == DialogResult.OK)
                manager.SetPackageDescription(version, frm.Titel, frm.Description, frm.DescriptionType);
        }

        private void ButtonItem_BotSettings_Click(object sender, EventArgs e)
        {
            var frm = new DiscordBotSettingsWindow(General.CurProject.DiscordBotConfig);
            if (frm.ShowDialog(this) == DialogResult.OK)
            {
                if (discordBot is not null)
                    LoadDiscordBot();
            }
        }

        private void ButtonItem_ProxyConfig_Click(object sender, EventArgs e)
        {
            var frm = new ProxyConfigEditor(General.CurProject.ProxyConfig);
            if (frm.ShowDialog(this) == DialogResult.OK)
                General.SetProxyConfig();
        }

        private void RadListView_Packages_SelectedItemChanged(object sender, EventArgs e)
        {
            var anySelected = radListView_Packages.SelectedItem is not null;
            radRibbonBarGroup_Discord.Enabled = anySelected;
            radRibbonBarGroup_PackageManagement.Enabled = anySelected;
        }

        private void RadListView1_CellFormatting(object sender, ListViewCellFormattingEventArgs e)
        {
            e.CellElement.TextAlignment = System.Drawing.ContentAlignment.MiddleLeft;
        }

        private void RadListView1_CellFormatting_1(object sender, ListViewCellFormattingEventArgs e)
        {
            e.CellElement.TextAlignment = System.Drawing.ContentAlignment.MiddleLeft;
        }

        private void RadPageView1_SelectedPageChanged(object sender, EventArgs e)
        {
            var selPage = radPageView1.SelectedPage;

            if (selPage == radPageViewPage_Extensions)
            {
                radRibbonBarGroup_PackageFiles.Visibility = ElementVisibility.Collapsed;
                radRibbonBarGroup_PackageExtensions.Visibility = ElementVisibility.Visible;
            }
            else if (selPage == radPageViewPage_Packaging)
            {
                radRibbonBarGroup_PackageFiles.Visibility = ElementVisibility.Visible;
                radRibbonBarGroup_PackageExtensions.Visibility = ElementVisibility.Collapsed;
            }
        }

        // F e a t u r e s   -  P a c k a g i n g

        private void ShowAllPackageTemplateConfig()
        {
            ShowPackageFiles();
            ShowPackageExtensions();
        }

        private void ShowPackageFiles()
        {
            radTreeView_PackagingFiles.BeginUpdate();
            radTreeView_PackagingFiles.Nodes.Clear();

            if (!string.IsNullOrEmpty(packageManager.FilesToCopyPath))
            {
                Action<RadTreeNodeCollection, string> nodeCreation = null;
                nodeCreation = (parentCollection, p) =>
                {
                    bool isDir = (File.GetAttributes(p) & FileAttributes.Directory) == FileAttributes.Directory;
                    var n = new RadTreeNode()
                    {
                        Tag = p,
                        Text = radTreeView_PackagingFiles.Nodes == parentCollection ? p : Path.GetFileName(p),
                        ImageIndex = isDir ? 0 : 1
                    };
                    parentCollection.Add(n);
                    if (isDir)
                    {
                        var dirInfo = new DirectoryInfo(p);
                        dirInfo.EnumerateDirectories().ForEach(di => nodeCreation(n.Nodes, di.FullName));
                        dirInfo.EnumerateFiles().ForEach(fi => nodeCreation(n.Nodes, fi.FullName));
                    }
                };
                nodeCreation(radTreeView_PackagingFiles.Nodes, packageManager.FilesToCopyPath);
            }

            radTreeView_PackagingFiles.EndUpdate();
        }

        private void ShowPackageExtensions()
        {
            radListView_Extensions.BeginUpdate();
            radListView_Extensions.Items.Clear();

            foreach (string fAddOn in packageManager.GetAllUpdateInstallerÁddOn())
            {
                var cells = new List<string>();
                cells.Add(Path.GetFileName(fAddOn));
                cells.Add(Path.GetDirectoryName(fAddOn));

                var item = new ListViewDataItem(string.Empty, cells.ToArray())
                {
                    Tag = fAddOn
                };

                radListView_Extensions.Items.Add(item);
            }

            radListView_Extensions.EndUpdate();
        }

        private IEnumerable<string> GetSelectedUpdateInstallAddOns()
        {
            var list = new List<string>();
            foreach (var item in radListView_Extensions.SelectedItems)
                list.Add(Conversions.ToString(item.Tag));
            return list;
        }

        private void NewPackageTemplate()
        {
            packageManager.NewTemplate();
            curPackageTemplatePath = string.Empty;
            ShowAllPackageTemplateConfig();
        }

        private void OpenPackageTemplate()
        {
            var ofd_UpdateAdmin_LoadTemplate = new RadOpenFileDialog() { Filter = FILTER_PACKAGE_TEMPLATE };
            if (ofd_UpdateAdmin_LoadTemplate.ShowDialog() == DialogResult.OK)
            {
                packageManager.LoadTemplate(ofd_UpdateAdmin_LoadTemplate.FileName);
                curPackageTemplatePath = ofd_UpdateAdmin_LoadTemplate.FileName;
                ShowAllPackageTemplateConfig();
            }
        }

        private void SavePackageTemplate()
        {
            if (string.IsNullOrEmpty(curPackageTemplatePath))
            {
                SavePackageTemplateAs();
            }
            else
            {
                packageManager.SaveTemplate(curPackageTemplatePath);
            }
        }

        private void SavePackageTemplateAs()
        {
            var sfd_UpdateAdmin_SaveTemplate = new RadSaveFileDialog() { Filter = FILTER_PACKAGE_TEMPLATE };
            if (sfd_UpdateAdmin_SaveTemplate.ShowDialog() == DialogResult.OK)
            {
                packageManager.SaveTemplate(sfd_UpdateAdmin_SaveTemplate.FileName);
                curPackageTemplatePath = sfd_UpdateAdmin_SaveTemplate.FileName;
            }
        }

        private void SelectPackageFileFolder()
        {
            var ofd_UpdateAdmin_PkgFileFolder = new RadOpenFolderDialog();
            if (ofd_UpdateAdmin_PkgFileFolder.ShowDialog() == DialogResult.OK)
            {
                packageManager.FilesToCopyPath = ofd_UpdateAdmin_PkgFileFolder.FileName;
                ShowPackageFiles();
            }
        }

        private void RemovePackageFileFolder()
        {
            packageManager.FilesToCopyPath = string.Empty;
            ShowPackageFiles();
        }

        private async Task<bool> ExportUpdatePackage(string filePath)
        {
            bool success = false;
            ProgressPackagingControls(true);

            try
            {
                await Task.Run(() => packageManager.ExportPackage(filePath));
                success = true;
            }
            catch (Exception)
            {
                success = false;
            }

            ProgressPackagingControls(false);
            return success;
        }

        private async Task<bool> ExportTempUpdatePackage()
        {
            var filePath = Path.GetTempFileName();
            bool res = await ExportUpdatePackage(filePath);
            if (res) TempPackageFilePath = filePath;
            return res;
        }

        private void AddUpdateInstallerExtension()
        {
            var ofd_UpdateAdmin_AddExtension = new RadOpenFileDialog()
            {
                MultiSelect = true,
                Filter = FILTER_PACKAGE_ADDON
            };
            if (ofd_UpdateAdmin_AddExtension.ShowDialog() == DialogResult.OK)
            {
                foreach (string f in ofd_UpdateAdmin_AddExtension.FileNames)
                {
                    if (!packageManager.AddUpdateInstallerAddOn(f))
                    {
                        RadMessageBox.Show(My.Resources.UpdatingAdministrationLangRes.MsgBox_ErrorAddingInstallerAddOn, My.Resources.UpdatingAdministrationLangRes.MsgBox_Error_Titel, MessageBoxButtons.OK, RadMessageIcon.Error);
                    }
                }
            }
        }

        private void RemoveUpdateInstallerExtension()
        {
            foreach (string fAddOn in GetSelectedUpdateInstallAddOns())
                packageManager.RemoveUpdateInstallerAddOn(fAddOn);
        }

        // G u i   -   P a c k a g i n g

        private void ButtonItem_Pkg_NewTemplate_Click(object sender, EventArgs e)
        {
            NewPackageTemplate();
        }

        private void ButtonItem_Pkg_OpenTemplate_Click(object sender, EventArgs e)
        {
            OpenPackageTemplate();
        }

        private void ButtonItem_Pkg_SaveTemplate_Click(object sender, EventArgs e)
        {
            SavePackageTemplate();
        }

        private void ButtonItem_Pkg_SaveTemplateAs_Click(object sender, EventArgs e)
        {
            SavePackageTemplateAs();
        }

        private void ButtonItem_Pkg_SelectFileFolder_Click(object sender, EventArgs e)
        {
            SelectPackageFileFolder();
        }

        private void ButtonItem_Pkg_RemoveFileFolder_Click(object sender, EventArgs e)
        {
            RemovePackageFileFolder();
        }

        private async void ButtonItem_Pkg_Export_Click(object sender, EventArgs e)
        {
            var sfd_UpdateAdmin_ExportPkg = new RadSaveFileDialog()
            {
                Filter = FILTER_PACKAGE_ZIP_PACKAGE
            };

            if (sfd_UpdateAdmin_ExportPkg.ShowDialog() == DialogResult.OK)
            {
                if (await ExportUpdatePackage(sfd_UpdateAdmin_ExportPkg.FileName))
                    RadMessageBox.Show(this, My.Resources.UpdatingAdministrationLangRes.MsgBox_PkgExportSuccess, My.Resources.UpdatingAdministrationLangRes.MsgBox_PkgExportSuccess_Titel, MessageBoxButtons.OK, RadMessageIcon.Info);
            }
        }

        private void ButtonItem_Pkg_AddExtension_Click(object sender, EventArgs e)
        {
            AddUpdateInstallerExtension();
        }

        private void ButtonItem_Pkg_RemoveExtension_Click(object sender, EventArgs e)
        {
            RemoveUpdateInstallerExtension();
        }
    }
}