using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Configuration;
using System.Linq; 
using System.Text.RegularExpressions;
using System.Web.Routing;
using log4net;
using Helpers.Log;
using System.Reflection; 

/// <summary>
/// Summary description for WebApp
/// </summary>
public static class WebApp
{
    private static ILog logger = Logger.GetLogger(MethodBase.GetCurrentMethod().DeclaringType.Name);

    public enum RolesEnum
    {
        BASICUSERROLE, 
        SUPERVISORUSERROLE,
        ADMINUSERROLE,
    }

    private static string connectionString = @"";
    static WebApp()
    {
        connectionString = ConfigurationManager.ConnectionStrings["SYSTEM"].ConnectionString;
    }
    
    private static bool demoMode = false;
    public static bool IsDemo()
    {
        return demoMode;
    }

    public static void CheckSiteLanguageCookie()
    {
        Page page = (Page)HttpContext.Current.Handler;

        if (page.Request.Cookies["siteLanguage"] == null)
        {
            HttpCookie lang = new HttpCookie("siteLanguage", "EN");
            lang.Expires = DateTime.Now.AddDays(30);
            page.Response.Cookies.Add(lang);
        }
    }

    public static string GetUserName(HttpRequest req = null)
    {
        string username = string.Empty;
        try
        {
            if (req == null)
            {
                req = HttpContext.Current.Request;
            }

            if (req.Cookies["loginData"] != null)
            {
                username = req.Cookies["loginData"]["username"];
            }
        }
        catch (Exception ex) { logger.Error(ex); }
        return username;
    }

    public static List<String> GetUserRoles(HttpRequest req = null)
    {
        List<string> roles = new List<string>();
        try
        {
            if (req == null)
            {
                req = HttpContext.Current.Request;
            }

            if (req.Cookies["loginData"] != null)
            {
                string rolesString = req.Cookies["loginData"]["roles"];
                roles.AddRange(rolesString.ToUpper().Split(',')); 
            }
        }
        catch (Exception ex) { logger.Error(ex); }
        return roles;
    }

    public class MenuItemProperties : List<MenuItemProperty>
    {
        public List<MenuItemProperty> GetDataProperties()
        {
            try
            {
                return this.Where(t => t.Name.ToUpper().StartsWith("DATA-")).ToList(); 
            }
            catch (Exception ex)
            {
                logger.Error(ex);

                return new List<MenuItemProperty>();
            }
        }

        public MenuItemProperty GetProperty(string name){
            try
            {
                return this.FirstOrDefault(t => t.Name.ToUpper() == name.ToUpper());
            }
            catch (Exception ex)
            {
                logger.Error(ex);

                return null;
            }
        }

        public string GetPropertyValue(string name)
        {
            try
            {
                MenuItemProperty mip = this.FirstOrDefault(t => t.Name.ToUpper() == name.ToUpper());
                if (mip != null) return mip.Value;
                else return null; 
            }
            catch (Exception ex)
            {
                logger.Error(ex);

                return null;
            }
        }

        public string GetLocalizePropertyValue(string name = "localize-key")
        {
            try
            {
                string value = GetPropertyValue(name);
                value = value.Contains(".Text") ? value : value + ".Text";

                return value; 
            }
            catch (Exception ex)
            {
                logger.Error(ex);

                return string.Empty;
            }
        }

        public bool HasProperty(string name)
        {
            try
            {
                return this.Exists(t => t.Name.ToUpper() == name.ToUpper()); 
            }
            catch (Exception ex)
            {
                logger.Error(ex);

                return false;
            }
        }
    }

    public class MenuItem{
        public int Id { get; set; }
        public int? Parent { get; set; }
        public string Name { get; set; }
        public int? RouteId { get; set; }
        public bool Enabled { get; set; }
        public bool Visible { get; set; }
        public string Group { get; set; }

        public string Route { get; set; }
        public string URL { get; set; }

        public List<MenuItem> Children { get; set; }

        public List<MenuItemParameter> Parameters { get; set; }
        public MenuItemProperties Properties { get; set; }

        public MenuItem()
        {
            this.Children = new List<MenuItem>(); 
            this.Parameters = new List<MenuItemParameter>();
            this.Properties = new MenuItemProperties(); 
        }
    }

    public class MenuItemProperty{
        public string Name { get; set; }
        public string Value { get; set; }
    }

    public class MenuItemParameter{
        public string Name { get; set; }
        public string Value { get; set; }
    }

    public class MenuData{
        public List<MenuItem> Items { get; set; }

        public MenuData(){
            this.Items = new List<MenuItem>(); 
        }

        public List<MenuItem> GetGroupItems(string groupName)
        {
            return this.Items.Where(t => {
                if (!string.IsNullOrEmpty(t.Group)){
                    string groups = t.Group.ToUpper();
                    groups = Regex.Replace(groups, @"\s+", "");

                    return groups.Split(',').Contains(groupName.ToUpper().Trim()); 
                }

                return false; 
            }).ToList(); 
        }
    }

    public static MenuData GetMenu(){
        MenuData md = new MenuData();
        try
        {
            List<List<Dictionary<string, string>>> data = DAL.GetMenuData(GetUserName());

            List<Dictionary<string, string>> items = new List<Dictionary<string, string>>();
            List<Dictionary<string, string>> itemParameters = new List<Dictionary<string, string>>();
            List<Dictionary<string, string>> itemProperties = new List<Dictionary<string, string>>();

            items = (data.Count > 0) ? data[0] : items;
            itemParameters = (data.Count > 1) ? data[1] : itemParameters; 
            itemProperties = (data.Count > 2) ? data[2] : itemProperties;

            Dictionary<int, List<MenuItemParameter>> parameters = new Dictionary<int,List<MenuItemParameter>>(); 
            Dictionary<int, MenuItemProperties> properties = new Dictionary<int,MenuItemProperties>(); 

            itemParameters.ForEach(ip => {
                int id = Convert.ToInt32(ip["MenuLinkID"]);
                if (!parameters.ContainsKey(id)){
                    parameters.Add(id, new List<MenuItemParameter>()); 
                }

                parameters[id].Add(new MenuItemParameter{
                    Name = ip["Name"], Value = ip["Value"]
                }); 
            }); 

            itemProperties.ForEach(ip => {
                int id = Convert.ToInt32(ip["MenuId"]);
                if (!properties.ContainsKey(id)){
                    properties.Add(id, new MenuItemProperties()); 
                }

                properties[id].Add(new MenuItemProperty{
                    Name = ip["Name"], Value = ip["Value"]
                }); 
            });



            //for each item
            Dictionary<int, MenuItem> itemsById = new Dictionary<int, MenuItem>();

            for (int i = 0; i < items.Count; i++)
            {
                Dictionary<string, string> row = items[i];
                MenuItem mi = new MenuItem()
                {
                    Id = Convert.ToInt32(row["Id"]), 
                    Parent = !string.IsNullOrEmpty(row["Parent"]) ? (int?) Convert.ToInt32(row["Parent"]) : null,
                    Name = !string.IsNullOrEmpty(row["Name"]) ? row["Name"] : null, 
                    RouteId = !string.IsNullOrEmpty(row["RouteID"]) ? (int?) Convert.ToInt32(row["RouteID"]) : null, 
                    Enabled = Convert.ToBoolean(row["Enabled"]), 
                    Visible = Convert.ToBoolean(row["Visible"]), 
                    Group = !string.IsNullOrEmpty(row["Group"]) ? row["Group"] : null, 
                    Route = row["Route"]
                };

                mi.Parameters = (parameters.ContainsKey(mi.Id)) ? parameters[mi.Id] : mi.Parameters;
                mi.Properties = (properties.ContainsKey(mi.Id)) ? properties[mi.Id] : mi.Properties; 

                mi.URL = ResolveURL(mi.Route, mi.Parameters);

                itemsById.Add(mi.Id, mi);
            }

            foreach (KeyValuePair<int, MenuItem> kvp in itemsById)
            {
                MenuItem mi = kvp.Value;

                if (mi.Parent != null)
                {
                    MenuItem parent = null; 

                    if (itemsById.ContainsKey(mi.Parent.Value) == true)
                        itemsById[mi.Parent.Value].Children.Add(mi);
                }
                else {
                    md.Items.Add(mi); 
                }
            }
        }
        catch (Exception ex) {
            logger.Debug(ex); 
        }

        return md; 
    }

    public static string ResolveURL(string route, List<MenuItemParameter> parameters)
    {
        string url = route;
        try
        {
            Regex regExpAllNormalParams = new Regex("(\\(\\?)?\\/:\\w+");
            Regex regExpAllOptionalParams = new Regex("\\((.*?)\\)");

            parameters.ForEach(p =>
            {
                Regex regExpNormal = new Regex("(\\(\\?)?(:" + p.Name + ")([\\/\\(].+)?$");
                Regex regExpOptional = new Regex("\\((/:" + p.Name + ")\\)");
                Regex regExpNamedParams = new Regex("\\((/([^\\/]+):" + p.Name + ")\\)");

                if (regExpNamedParams.IsMatch(url))
                {
                    //regExpNamedParams.Replace(url, "/" + (namedParamsMatch ? namedParamsMatch[2] : "")
                }
                else if (regExpOptional.IsMatch(url))
                {
                    url = regExpOptional.Replace(url, "/" + p.Value); 
                }
                else if (regExpNormal.IsMatch(url))
                {
                    url = regExpNormal.Replace(url, p.Value); 
                }
            });

            url = regExpAllOptionalParams.Replace(url, string.Empty);
            url = regExpAllNormalParams.Replace(url, string.Empty); 
        }
        catch (Exception ex)
        {
            logger.Error(ex);

            url = string.Empty; 
        }

        return url; 
    }

    public class SecurityAction
    {
        public int Id { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
        public bool Value { get; set; }
    }

    public class SecurityModule
    {
        public int Id { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
        public bool Value { get; set; }
        public List<SecurityAction> Actions { get; set; }

        public SecurityModule(){
            this.Actions = new List<SecurityAction>(); 
        }

        public SecurityAction GetAction(string code)
        {
            SecurityAction sa = null;
            try
            {
                sa = this.Actions.First(t => t.Code.ToUpper().Equals(code.ToUpper())); 
            }
            catch (Exception ex)
            {
                logger.Error(ex);

                return null; 
            }

            return sa; 
        }
    }

    public class SecurityData
    {
        public List<SecurityModule> Modules { get; set; }
        
        public SecurityData()
        {
            this.Modules = new List<SecurityModule>(); 
        }

        public SecurityModule GetModule(string code){
            SecurityModule sm = null; 
            try
            {
                sm = this.Modules.FirstOrDefault(t => t.Code.ToUpper().Equals(code.ToUpper()));
            }
            catch (Exception ex)
            {
                logger.Error(ex);

                return null; 
            }

            return sm; 
        }

        public SecurityAction GetModuleAction(string module, string action)
        {
            SecurityAction sa = null; 
            try
            {
                SecurityModule sm = this.GetModule(module);
                if (sm != null)
                {
                    sa = sm.GetAction(action); 
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex);

                return null; 
            }
            
            return sa; 
        }

        public bool GetModuleActionValue(string module, string action)
        {
            bool ret = false;
            try
            {
                SecurityAction sa = this.GetModuleAction(module, action);
                if (sa != null)
                {
                    ret = sa.Value; 
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex);

                return false; 
            }

            return ret; 
        }
    }

    public static SecurityData GetSecurityData()
    {
        SecurityData md = new SecurityData();
        try
        {
            List<List<Dictionary<string, string>>> data = DAL.GetUserSecurityModulesActions(GetUserName()); 

            List<Dictionary<string, string>> modules = new List<Dictionary<string, string>>();
            List<Dictionary<string, string>> moduleActions = new List<Dictionary<string, string>>();

            modules = (data.Count > 0) ? data[0] : modules;
            moduleActions = (data.Count > 1) ? data[1] : moduleActions;

            Dictionary<int, List<SecurityAction>> actions = new Dictionary<int, List<SecurityAction>>();

            moduleActions.ForEach(action =>
            {
                int id = Convert.ToInt32(action["ModuleId"]);
                if (!actions.ContainsKey(id))
                {
                    actions.Add(id, new List<SecurityAction>());
                }

                actions[id].Add(new SecurityAction
                {
                    Id = Convert.ToInt32(action["ActionId"]), 
                    Name = action["Name"], 
                    Code = action["Code"], 
                    Value = Convert.ToBoolean(action["Value"])
                });
            });

            //for each item
            for (int i = 0; i < modules.Count; i++)
            {
                Dictionary<string, string> row = modules[i];
                SecurityModule sm = new SecurityModule()
                {
                    Id = Convert.ToInt32(row["ModuleId"]),
                    Code = !string.IsNullOrEmpty(row["Code"]) ? row["Code"] : null, 
                    Name = !string.IsNullOrEmpty(row["Name"]) ? row["Name"] : null,
                    Value = Convert.ToBoolean(row["Value"])
                };

                sm.Actions = (actions.ContainsKey(sm.Id)) ? actions[sm.Id] : sm.Actions;

                md.Modules.Add(sm); 
            }
        }
        catch (Exception ex) { logger.Error(ex); }

        return md;
    }

    public static void AuditUserLoggedIn(string username)
    {
        try
        {
            bool status = DAL.UserLoggedIn(username); 
        }
        catch (Exception ex) { logger.Error(ex); }
    }

    public static string GetSystemConnectionString()
    {
        string domain = WebApp.GetUserDomain();

        if (string.IsNullOrWhiteSpace(domain) == false)
            return ConfigurationManager.ConnectionStrings["SYSTEM-" + domain].ConnectionString;
        else
            return ConfigurationManager.ConnectionStrings["SYSTEM"].ConnectionString;
    }

    public static string GetUserDomain(HttpRequest req = null)
    {
        string domain = string.Empty;
        try
        {
            if (req == null)
            {
                req = HttpContext.Current.Request;
            }

            if (req.Cookies["loginData"] != null)
            {
                domain = req.Cookies["loginData"]["domain"];
            }
        }
        catch (Exception ex) { logger.Error(ex); }
        return domain;
    }
}