using ClientModelsPrivate.Main.Menus;
using ExtensionModules.Interfaces;
using ExtensionModules.Interfaces.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using FlowControl.Builder.Elements;
using ParusClient.Activities;
using System.Xml;
using System.IO;
using CommonActivities.Activities;
using Common.Extensions;
using CommonActivities.Activities.Common;
using FlowControl.Core;
using Parus.Database.Specialized;
using CommonWeb.Workflow.Extensioins;
using System.Web;

//"Панели" - расширение для "ПАРУС 8 Онлайн" (библиотека для сервера приложений)
namespace P8PanelsParusOnlineExt
{
    //Вспомогательные методы
    public static class P8PanelUtils
    {
        //Имена специальных параметров "встроенных" методов
        public const string SPECIAL_PRM_CAPTION = "SCAPTION";
        public const string SPECIAL_PRM_PANEL = "SPANEL";
        public const string SPECIAL_PRM_IDENT = "NIDENT";

        //Формирование адреса панели
        public static string BuildPanelURL(P8PanelConfig config, P8Panel panel, IDictionary<string, object> parameters = null)
        {
            string panelUrl = config.panelsUrlBase;
            if (!panelUrl.EndsWith("/") && !panel.url.StartsWith("/")) panelUrl += "/";
            panelUrl += panel.url;
            if (parameters != null)
            {
                var prms = "";
                foreach (KeyValuePair<string, object> entry in parameters)
                {
                    if ((entry.Key != SPECIAL_PRM_CAPTION) && (entry.Key != SPECIAL_PRM_PANEL))
                        prms += entry.Key + "=" + HttpUtility.UrlEncode(Convert.ToString(parameters.SafeValue(entry.Key))) + "&";
                }
                if (!string.IsNullOrEmpty(prms)) panelUrl += "?" + prms;
            }
            return panelUrl;
        }
    }

    //Настройки расширения
    [Serializable]
    public class P8PanelConfig
    {
        private List<P8PanelMenuApp> _menuApps = new List<P8PanelMenuApp>();
        private List<P8PanelMenuItem> _menuItems = new List<P8PanelMenuItem>();
        private List<P8Panel> _panels = new List<P8Panel>();
        private string _panelsUrlBase;

        //Конструктор
        public P8PanelConfig(string confiFileName)
        {
            //Читаем указанный файл конфигурации как XML
            XmlDocument doc = new XmlDocument();
            doc.Load(confiFileName);
            XmlNode section = doc.DocumentElement.SelectSingleNode("/CITK.P8Panels");
            //Обходим десериализованный XML
            foreach (XmlNode sectionNode in section.ChildNodes)
            {
                //Настройки пунктов меню приложений
                if (sectionNode.Name == "MenuItems")
                {
                    foreach (XmlNode menuAppNode in sectionNode.ChildNodes)
                    {
                        _menuApps.Add(new P8PanelMenuApp() { name = menuAppNode.Attributes["name"].Value });
                        foreach (XmlNode menuItemsNode in menuAppNode.ChildNodes)
                        {
                            _menuItems.Add(new P8PanelMenuItem()
                            {
                                app = menuAppNode.Attributes["name"].Value,
                                parent = menuItemsNode.Attributes["parent"].Value,
                                separator = menuItemsNode.Attributes["separator"] != null ? bool.Parse(menuItemsNode.Attributes["separator"].Value) : false,
                                name = menuItemsNode.Attributes["name"]?.Value,
                                caption = menuItemsNode.Attributes["caption"]?.Value,
                                url = menuItemsNode.Attributes["url"]?.Value,
                                panelName = menuItemsNode.Attributes["panelName"]?.Value
                            });
                        }
                    }
                }
                //Настройки панелей
                if (sectionNode.Name == "Panels")
                {
                    _panelsUrlBase = sectionNode.Attributes["urlBase"].Value;
                    foreach (XmlNode panelNode in sectionNode.ChildNodes)
                    {
                        _panels.Add(new P8Panel()
                        {
                            name = panelNode.Attributes["name"].Value,
                            caption = panelNode.Attributes["caption"].Value,
                            url = panelNode.Attributes["url"].Value,
                            path = panelNode.Attributes["path"].Value,
                            icon = panelNode.Attributes["icon"].Value,
                            showInPanelsList = panelNode.Attributes["showInPanelsList"] != null ? bool.Parse(panelNode.Attributes["showInPanelsList"].Value) : false,
                        });
                    }
                }
            }
        }

        //Поиск панели в настройке по наименованию
        public P8Panel FindPanelByName(string name)
        {
            return _panels.Find(panel => panel.name == name);
        }

        //Список приложений для подключения панелей
        public List<P8PanelMenuApp> menuApps { get => _menuApps; }

        //Список подключаемых к приложениям пунктов меню панелей
        public List<P8PanelMenuItem> menuItems { get => _menuItems; }

        //Настройки панелей
        public List<P8Panel> panels { get => _panels; }

        //Базовый URL к WEB-приложению "Парус 8 - Панели мониторинга"
        public string panelsUrlBase { get => _panelsUrlBase; }
    }

    //Приложение панели
    [Serializable]
    public class P8PanelMenuApp
    {
        public string name { get; set; }
    }

    //Элемент меню панели
    [Serializable]
    public class P8PanelMenuItem
    {
        public string app { get; set; }
        public string parent { get; set; }
        public bool separator { get; set; }
        public string name { get; set; }
        public string caption { get; set; }
        public string url { get; set; }
        public string panelName { get; set; }
    }

    //Параметры панели
    [Serializable]
    public class P8Panel
    {
        public string name { get; set; }
        public string caption { get; set; }
        public string url { get; set; }
        public string path { get; set; }
        public string icon { get; set; }
        public bool showInPanelsList { get; set; }
    }

    //Обработчик "встроенного" метода открытия панели
    public class OpenPanelActivity : OpenExternalLinkActivity
    {
        private P8PanelConfig _pconf;
        private readonly IContextualParusDatabaseFactoryProvider _contextualParusDatabaseFactoryProvider;
        private string _contextCaption;
        private string _contextLink;

        //Конструктор
        public OpenPanelActivity(string caption, P8PanelConfig pconf, IContextualParusDatabaseFactoryProvider contextualParusDatabaseFactoryProvider) : base(caption, "stub")
        {
            _pconf = pconf ?? throw new ArgumentNullException("pconf");
            _contextualParusDatabaseFactoryProvider = contextualParusDatabaseFactoryProvider ?? throw new ArgumentNullException("contextualParusDatabaseFactoryProvider");
        }

        //Исполнение действия
        protected override void InternalExecute(IContext context)
        {
            //Читаем параметры из контекста
            if (context == null) throw new ArgumentNullException("context");
            var parameters = context.SafeParameterValue("MethodParameterValues") as IDictionary<string, object>;
            var nIdent = parameters.SafeValue(P8PanelUtils.SPECIAL_PRM_IDENT);
            var sPanel = (string) parameters.SafeValue(P8PanelUtils.SPECIAL_PRM_PANEL);
            _contextCaption = (string)parameters.SafeValue(P8PanelUtils.SPECIAL_PRM_CAPTION);
            //Определяем панель, которую будем открывать
            if (string.IsNullOrEmpty(sPanel)) throw new Exception("Не указан код панели. Проверьте, что задано значение параметра \"SPANEL\".");
            P8Panel panel = _pconf.FindPanelByName(name: sPanel);
            //Если панель определена
            if (panel != null)
            {
                //Если был указан идентификатор отмеченных записей
                if (nIdent != null)
                {
                    //Скопируем системный буфер отмеченных в буфер для панелей (чтобы не потерять при завершении транзакции)
                    var parusDatabaseFactory = _contextualParusDatabaseFactoryProvider.GetDatabaseFactory();
                    using (var connection = parusDatabaseFactory.CreateConnection())
                    {
                        using (var command = parusDatabaseFactory.CreateProcedure(connection, "PKG_P8PANELS.SELECTLIST_INIT"))
                        {
                            command.Parameters.Add(P8PanelUtils.SPECIAL_PRM_IDENT, (long) nIdent);
                            command.ExecuteNonQuery();
                        }
                    }
                }
                //Если не передали заголовок панели в параметрах - используем её заголовок из конфигурации
                if (string.IsNullOrEmpty(_contextCaption)) _contextCaption = panel.caption;
                //Сформируем ссылку на панель
                _contextLink = P8PanelUtils.BuildPanelURL(config: _pconf, panel: panel, parameters: parameters);
            } else throw new Exception($"Панель \"{sPanel}\" не определена.");
            //Выполним алгоритм родительского класса
            base.InternalExecute(context);
        }

        //Переопределение параметров открываемой закладки
        protected override IDictionary<string, object> CreateClientParameters(IContext context)
        {
            //Сформируем словарь параметров открываемой закладки
            var clientParameters = base.CreateClientParameters(context);
            //Если удалось сформировать заголовок - переопределим его
            if (!string.IsNullOrEmpty(_contextCaption)) clientParameters.AddOrReplace(WellknownOpenMainTabActivityParameterNames.Caption, _contextCaption);
            //Переопределим адрес панели тем, который сформировали
            clientParameters.AddOrReplace(WellknownOpenMainTabActivityParameterNames.Url, _contextLink);
            //Вернём измененный словарь параметров
            return clientParameters;
        }
    }

    //Точка входа в модуль расширения
    public class Module : ExtensionModuleBase
    {
        private static string _configFile = Path.GetDirectoryName(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile) + "\\Config\\p8panels.config";
        private static IList<IHook> _hooks = new List<IHook>();
        public override string ModuleName => "P8Panels";
        public override string AuthorInfo => "ЦИТК";
        public override IList<IHook> Hooks => _hooks;
        public override bool HasViews => false;

        //Конструктор
        public Module()
        {
            //Читаем и десериализуем настройки
            P8PanelConfig pconf = new P8PanelConfig(_configFile);
            //Вешаем хуки на создание элементов меню для всех упомянутых в настройках приложений
            pconf.menuApps.ForEach(menuApp => {
                _hooks.Add(MainMenuProcessorHook.Make(menuApp.name, mainMenu => {
                    pconf.menuItems.ForEach(menuItem => {
                        if (menuItem.app == menuApp.name)
                        {
                            var parentMenuItem = mainMenu.Root?.Children?.First(x => x.Value?.Name == menuItem.parent);
                            parentMenuItem.Children.Add(new MainMenuItemNode { Value = new MainMenuItem { IsSeparator = menuItem.separator, Name = menuItem.name, Caption = menuItem.caption } });
                        }
                    });
                    return mainMenu;
                }));
            });
            //Вешаем хуки на нажатие всех сформированных элементов меню
            Dictionary<string, Func<Sequence>> menuItemsActions = new Dictionary<string, Func<Sequence>>();
            pconf.menuItems.ForEach(menuItem => {
                if (!menuItem.separator)
                    if (menuItem.panelName == null)
                        menuItemsActions.Add(menuItem.name, () => new Sequence().Add<OpenExternalLinkActivity>(new { caption = menuItem.caption, url = menuItem.url }));
                    else
                    {
                        P8Panel panel = pconf.FindPanelByName(name: menuItem.panelName);
                        if (panel != null)
                            menuItemsActions.Add(menuItem.name, () => new Sequence().Add<OpenExternalLinkActivity>(new { caption = panel.caption, url = P8PanelUtils.BuildPanelURL(config: pconf, panel: panel) }));
                        else
                            menuItemsActions.Add(menuItem.name, () => new Sequence().Add<MessageActivity>(new { caption = "Панель не определена", text = $"Панель \"{menuItem.panelName}\" не определена." }));
                    }

            });
            _hooks.Add(MainMenuItemBuilderHook.Make(menuItemsActions));
            //Хуки "встроенных" действий с панелями в разделах
            _hooks.Add(MethodBuilderHook.Make(new Dictionary<string, Func<Sequence>>{
                {
                    "P8PANELS_OPEN",
                    () => new Sequence().Add<OpenPanelActivity>(new { caption = "Панель", pconf = pconf })
                }
            }));
        }

        //Путь к файлу конфигурации расширения
        public static string configFile { get => _configFile; }
    }
}
