using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Net.Mail;
using System.IO;
using System.Xml;
using Newtonsoft.Json;
using Foresight.Data;
using Foresight.Security.License;
using FI.FIC;
using IronIntel.Contractor.Users;
using FI.FIC.Contracts.DataObjects.Enumeration;
using System.Web;
using System.Runtime.Serialization.Formatters.Binary;
using Foresight.Fleet.Services.Styles;
using IronIntel.Contractor.iisitebase;
using Foresight.Standard.Units;
using Foresight.Standard.Data;
using Foresight.Fleet.Services;
using FleetUser = Foresight.Fleet.Services.User;

namespace IronIntel.Contractor
{
    public class IronIntelHost : IFICHost
    {
        static readonly Guid FIC_MODULE_ID = new Guid("1c6dfe25-347d-4889-ab75-73ade9190d27");
        const string FIC_MODULE_NAME = "Foresight Intelligence Center";
        const string FIC_MODULE_VERSION = "3.0";

        public string FICDbConnectionString
        {
            get
            {
                return SystemParams.FICDbConnectionString;
            }
        }

        public string FrsDbConnectionString
        {
            get
            {
                return string.Empty;
            }
        }
        public bool FICInstalled
        {
            get
            {
                return true;
            }
        }

        public bool FRSInstalled
        {
            get
            {
                return false;
            }
        }

        public static void Init()
        {
            FICHostEnvironment.SetHost(new IronIntelHost());
        }

        private ConcurrentDictionary<string, Tuple<FICUserInfo, DateTime>> _Users = new ConcurrentDictionary<string, Tuple<FICUserInfo, DateTime>>(StringComparer.OrdinalIgnoreCase);

        public byte[] GetCacheData(string key, bool ignoreExpired, ref DateTime createTime)
        {
            byte[] buffer = CacheManager.GetValue(key);
            if (buffer == null || buffer.Length == 0)
            {
                return null;
            }
            byte[] tmp = Foresight.Security.SecurityHelper.Decompress(buffer);
            FICCacheObject fc = new FICCacheObject();
            fc.FromBuffer(tmp);
            createTime = fc.CreateTime;
            return fc.Data;
        }

        public DataTable GetCacheDataTable(string key, bool ignoreExpired, ref DateTime createTime)
        {
            byte[] buffer = GetCacheData(key, ignoreExpired, ref createTime);
            if (buffer == null)
            {
                return null;
            }
            using (MemoryStream ms = new MemoryStream(buffer, false))
            {
                try
                {
                    TableReader tr = new TableReader(ms);
                    return tr.Read();
                }
                catch
                {
                    return null;
                }
            }
        }

        public FICCompanyInfo GetCompanyInfo()
        {
            var cp = SystemParams.CustomerDetail;
            FICCompanyInfo ficcp = new FICCompanyInfo();
            ficcp.ID = cp.ID;
            ficcp.Name = cp.Name;

            return ficcp;
        }

        public CompanyLic GetLicense()
        {
            Foresight.Fleet.Services.Customer.LicenseInfo lic = SystemParams.GetLicense();
            if (lic == null)
            {
                return null;
            }
            CompanyLic ci = new CompanyLic();
            ci.CompanyID = SystemParams.CompanyID;
            ci.CompanyName = SystemParams.CustomerDetail.Name;

            LicenseInfo li = new LicenseInfo();
            ci.Licenses.Add(li);
            li.CompanyID = ci.CompanyID;
            li.CompanyName = ci.CompanyName;
            li.Expiration = lic.ExpireDate;
            li.ID = Guid.Empty;
            li.StartDate = lic.StartDate;
            li.ModuleID = FIC_MODULE_ID;
            li.ModuleName = FIC_MODULE_NAME;
            li.Version = FIC_MODULE_VERSION;

            foreach (var item in lic.Items)
            {
                var prop = ConvertLicenseItem(item);
                if (prop != null)
                {
                    li.AddtionalPropertes.Add(prop);
                }
            }

            return ci;
        }

        private LicenseAddtionalPropertyObj ConvertLicenseItem(Foresight.Fleet.Services.Customer.LicenseItem item)
        {
            if (item == null)
                return null;
            switch (item.Key)
            {
                case "ColumnLineCombChart":
                    return new LicenseAddtionalPropertyObj { Key = "ColumnLineCombChart", Value = item.Value, Description = item.Description };
                case "ExportChartToXPS":
                    return new LicenseAddtionalPropertyObj { Key = "ExportChartToXPS", Value = item.Value, Description = item.Description };
                case "FreeChart":
                    return new LicenseAddtionalPropertyObj { Key = "FreeChart", Value = item.Value, Description = item.Description };
                case "DrilldownToURL":
                    return new LicenseAddtionalPropertyObj { Key = "DrilldownToURL", Value = item.Value, Description = item.Description };
                case "MaxCharts":
                    return new LicenseAddtionalPropertyObj { Key = "MaxCharts", Value = item.Value, Description = item.Description };
                case "MaxDataTables":
                    return new LicenseAddtionalPropertyObj { Key = "MaxDataTables", Value = item.Value, Description = item.Description };
                case "PrintChart":
                    return new LicenseAddtionalPropertyObj { Key = "PrintChart", Value = item.Value, Description = item.Description };
                case "ScatterChart":
                    return new LicenseAddtionalPropertyObj { Key = "ScatterChart", Value = item.Value, Description = item.Description };
                case "Snapshot":
                    return new LicenseAddtionalPropertyObj { Key = "Snapshot", Value = item.Value, Description = item.Description };
                case "SQLGenerator":
                    return new LicenseAddtionalPropertyObj { Key = "SQLGenerator", Value = item.Value, Description = item.Description };
                case "EmailSubscribe":
                    return new LicenseAddtionalPropertyObj { Key = "EmailSubscribe", Value = item.Value, Description = item.Description };
                    //case "MainStyle":
                    //case "MaxAdminCount":
                    //case "MaxLogins":
                    //case "MaxNormalUerCount":
                    //case "MaxReadOnlyUserCount":
            }
            return null;
        }

        public string GetResourceLock(string resourceid, int locksecond)
        {
            return SystemParams.GetResourceLock(resourceid, locksecond);
        }

        private static FICUserInfo ConvertToFICUserInfo(IronIntel.Contractor.Users.UserInfo ui)
        {
            var user = new FICUserInfo
            {
                ID = ui.ID,
                IID = ui.IID,
                Enabled = ui.Active,
                DisplayName = ui.DisplayName,
                //Mobile = ui.Mobile,
                Mobile = ui.TextAddress,
                BusinessPhone = ui.BusinessPhone,
                UserLanguage = ui.PreferredLanguage,
                UserTimeZone = ui.TimeZone
            };
            switch (ui.UserType)
            {
                case UserTypes.Common:
                    user.UserType = FICUserTypes.Common;
                    break;
                case UserTypes.Admin:
                    user.UserType = FICUserTypes.Admin;
                    break;
                case UserTypes.Readonly:
                    user.UserType = FICUserTypes.Readonly;
                    break;
                case UserTypes.SupperAdmin:
                    user.UserType = FICUserTypes.SuperAdmin;
                    break;
                default:
                    user.UserType = FICUserTypes.Readonly;
                    break;
            }
            return user;
        }

        const int EXPIRESECONDS = 600;

        public FICUserInfo GetUserByIID(string useriid)
        {
            Tuple<FICUserInfo, DateTime> tp = null;
            _Users.TryGetValue(useriid, out tp);
            if (tp != null)
            {
                TimeSpan sp = DateTime.Now - tp.Item2;
                if (sp.TotalSeconds < EXPIRESECONDS)
                {
                    return tp.Item1;
                }
            }

            UserInfo ui = UserManagement.GetUserByIID(useriid);
            if (ui == null)
            {
                return null;
            }
            FICUserInfo u = ConvertToFICUserInfo(ui);
            _Users[u.IID] = new Tuple<FICUserInfo, DateTime>(u, DateTime.Now);
            return u;
        }

        public FICUserInfo GetUserByLoginSessionID(string sessionid)
        {
            UserInfo ui = UserManagement.GetUserBySessionID(sessionid);
            if (ui == null)
            {
                return null;
            }

            FICUserInfo u = ConvertToFICUserInfo(ui);
            _Users[u.IID] = new Tuple<FICUserInfo, DateTime>(u, DateTime.Now);
            return u;
        }

        public FICUserInfo GetUserByUserID(string userId)
        {
            Tuple<FICUserInfo, DateTime>[] users = _Users.Values.ToArray();
            Tuple<FICUserInfo, DateTime> tp = null;
            foreach (var user in users)
            {
                if (string.Equals(userId, user.Item1.ID, StringComparison.OrdinalIgnoreCase))
                {
                    tp = user;
                    break;
                }
            }
            if (tp != null)
            {
                TimeSpan sp = DateTime.Now - tp.Item2;
                if (sp.TotalSeconds < EXPIRESECONDS)
                {
                    return tp.Item1;
                }
            }

            UserInfo ui = UserManagement.GetUserByID(userId);
            if (ui == null)
            {
                return null;
            }
            FICUserInfo u = ConvertToFICUserInfo(ui);
            _Users[u.IID] = new Tuple<FICUserInfo, DateTime>(u, DateTime.Now);
            return u;
        }

        ConcurrentDictionary<string, FICUserInfo> _ForesightUsers = new ConcurrentDictionary<string, FICUserInfo>(StringComparer.OrdinalIgnoreCase);
        DateTime _LastRefreshForesightUsers = DateTime.MinValue;
        DateTime _LastGetAllUsersTime = DateTime.MinValue;

        public FICUserInfo[] GetUsers()
        {
            TimeSpan sp = DateTime.Now - _LastGetAllUsersTime;
            if (sp.TotalSeconds >= EXPIRESECONDS)
            {
                UserInfo[] users = UserManagement.GetUsers();
                foreach (UserInfo ui in users)
                {
                    FICUserInfo u = ConvertToFICUserInfo(ui);
                    _Users[u.IID] = new Tuple<FICUserInfo, DateTime>(u, DateTime.Now);
                }
                _LastGetAllUsersTime = DateTime.Now;
            }

            var items = _Users.Values.ToArray();
            List<FICUserInfo> ls = new List<FICUserInfo>();
            foreach (var u in items)
            {
                ls.Add(u.Item1);
            }
            return ls.ToArray();
        }

        private FICUserInfo[] GetForesightUsers()
        {
            TimeSpan sp = DateTime.Now - _LastRefreshForesightUsers;
            if (sp.TotalSeconds >= EXPIRESECONDS)
            {
                UserInfo[] foresightusers = UserManagement.GetForesightUsers();
                _ForesightUsers.Clear();
                TimeZoneInfo timezone = SystemParams.GetTimeZoneInfo("Foresight");
                foreach (var u in foresightusers)
                {
                    FICUserInfo fu = ConvertToFICUserInfo(u);
                    if (string.IsNullOrWhiteSpace(fu.UserTimeZone))
                    {
                        fu.UserTimeZone = timezone.Id;
                    }
                    _ForesightUsers[fu.IID] = fu;
                }
                _LastRefreshForesightUsers = DateTime.Now;
            }
            return _ForesightUsers.Values.ToArray();
        }

        public FICUserInfo[] GetUsers(bool hasAdmin)
        {
            if (!hasAdmin)
            {
                return GetUsers();
            }

            FICUserInfo[] localusers = GetUsers();
            FICUserInfo[] foresightusers = GetForesightUsers();
            List<FICUserInfo> ls = new List<FICUserInfo>();
            ls.AddRange(localusers);
            ls.AddRange(foresightusers);
            return ls.ToArray();
        }

        public FICUserInfo[] GetSimpleUsers(bool hasAdmin)
        {
            return GetUsers(hasAdmin);
        }

        public string GetUserEmail(string useriid)
        {
            FICUserInfo u = GetUserByIID(useriid);
            return u == null ? null : u.ID;
        }

        public void PostMessage(int category, string msg)
        {
            return;
        }

        public void ReleaseResourceLock(string lockid)
        {
            SystemParams.ReleaseResourceLock(lockid);
        }

        public void RemoveCache(string key)
        {
            CacheManager.Remove(key);
        }

        public void SendMail(MailMessage message)
        {
            try
            {
                SystemParams.SendMail("FIC", message);
            }
            catch (Exception ex)
            {
                SystemParams.WriteLog("Error", this.GetType().FullName + ".SendMail(MailMessage)", "Add fic mail to mail service failed", ex.ToString());
            }
        }

        public void SetCacheData(string key, byte[] buffer, int expirationsecond, bool slidingExpiration, DateTime createTime)
        {
            if (buffer == null)
            {
                RemoveCache(key);
                return;
            }
            FICCacheObject fc = new FICCacheObject();
            fc.Data = buffer;
            fc.CreateTime = createTime;

            byte[] tmp = Foresight.Security.SecurityHelper.Compress(fc.ToBuffer());
            CacheManager.SetValue(key, tmp, TimeSpan.FromSeconds(expirationsecond));
        }

        public void SetCacheDataTable(string key, DataTable dt, int expirationsecond, bool slidingExpiration, DateTime createTime)
        {
            if (dt == null)
            {
                RemoveCache(key);
            }
            else
            {
                if (dt.Rows.Count > 1000000)
                {
                    return;
                }
                using (MemoryStream ms = new MemoryStream())
                {
                    TableWriter tw = TableWriter.Create(dt);
                    tw.Write(ms);
                    ms.Position = 0;
                    byte[] buffer = ms.ToArray();
                    SetCacheData(key, buffer, expirationsecond, slidingExpiration, createTime);
                }
            }
        }

        public void SubscribeMessage(int category, Action<IEnumerable<MessageInformation>> action)
        {
            return;
        }

        public void WriteLog(string logType, string category, string source, string message, string detail)
        {
            SystemParams.WriteLog(logType, "FIC", source, message, detail);
        }

        public List<string> GetUserGroupIDByUserIID(string userIID)
        {
            return UserManagement.GetUserGroupIDByUserIID(userIID);
        }




        public LoginContext GetCurrentLoginContext(HttpContext context)
        {
            string session = IronIntelBasePage.GetLoginSessionID(context.Request);
            if (string.IsNullOrWhiteSpace(session))
            {
                return null;
            }

            LoginContext lc = new LoginContext();
            lc.SessionID = session;
            lc.User = GetUserByLoginSessionID(session);
            lc.LanguageID = GetLgID(context);
            return lc;
        }

        private string GetLgID(HttpContext context)
        {
            var language = context.Request.Cookies[Common.LanguageCookieName];
            if (language != null)
            {
                return language.Value;
            }
            return ResLanguage.ClientCurrentLanguage;
        }

        public string ProductEdition
        {
            get
            {
                return "General";
            }
        }

        public string FIExternalDBConnectionString
        {
            get
            {
                return string.Empty;
            }
        }

        public TimeSpan BaseUtcOffset
        {
            get
            {
                var timezone = CustomerTimeZoneInfo;
                return timezone.BaseUtcOffset;
            }
        }

        public TimeZoneInfo CustomerTimeZoneInfo
        {
            get
            {
                return SystemParams.CustomerDetail.TimeZone;
            }
        }

        public string FICommonDbConnectionString
        {
            get { return string.Empty; }
        }

        public PlatForms HostPlatForm
        {
            get { return PlatForms.Fleet; }
        }

        public string DefaultLanguageId
        {
            get
            {
                string lg = SystemParams.CustomerDetail.LanguageId;
                return string.IsNullOrWhiteSpace(lg) ? "en-us" : lg;
            }
        }

        public KeyValuePair<string, string>[] SupportedLanguages
        {
            get
            {
                return Foresight.Fleet.Services.CountryAndRegionCode.SupportedLanguages;
            }
        }

        public string GetStyleDefines(string useriid)
        {
            //StringBuilder s = new StringBuilder();
            //s.Append(@"<?xml version=""1.0"" encoding=""UTF-8""?>");
            //s.Append("<root>");
            //s.Append("<Workspace><Title><Background>#ff00ff</Background><Foreground>#222222</Foreground></Title></Workspace>");
            //s.Append("<Chart><Title><Background>#333333</Background><Foreground>#444444</Foreground></Title><Board>#555555</Board></Chart>");
            //s.Append("<Board><Title><Background>#666666</Background><Foreground>#777777</Foreground></Title></Board>");
            //s.Append("</root>");
            //return s.ToString();
            CustUIStyle uistyle = SystemParams.GetUIStyle(useriid);

            StringBuilder s = new StringBuilder();
            s.Append(@"<?xml version=""1.0"" encoding=""UTF-8""?>");
            s.Append("<root>");
            s.Append("<Workspace><Title><Background></Background><Foreground>#000000</Foreground></Title></Workspace>");
            s.Append("<Chart><Title><Background>" + uistyle.ChartTitleBackgroundColor + "</Background><Foreground></Foreground></Title><Board>" + uistyle.ChartBorderColor + "</Board></Chart>");
            //s.Append("<Board><Title><Background>#666666</Background><Foreground>#777777</Foreground></Title></Board>");
            s.Append("</root>");
            return s.ToString();
        }
        public Dictionary<string, string> GetAdditionalParameter()
        {
            return SystemParams.GetAdditionalParameter();
        }



        public SpecialDatabaseConnectionInfo[] GetSpecialDatabaseConnections()
        {
            const string IRONINTELMASTERDB = "10000000-0000-0000-0000-100000000000";
            const string REPORTDB = "10000000-0000-0000-0000-100000000001";
            // const string IATC = "10000000-0000-0000-0000-100000000002";

            List<SpecialDatabaseConnectionInfo> ls = new List<SpecialDatabaseConnectionInfo>();
            SpecialDatabaseConnectionInfo mast = new SpecialDatabaseConnectionInfo();
            mast.ID = IRONINTELMASTERDB;
            mast.DisplayName = "FleetView";
            mast.DatabaseType = "MSSQL";
            mast.ConnectionString = SystemParams.DataDbConnectionString;
            ls.Add(mast);

            string rptstr = SystemParams.GetIronIntelReportDataDbString();
            if (!string.IsNullOrWhiteSpace(rptstr))
            {
                SpecialDatabaseConnectionInfo rptdb = new SpecialDatabaseConnectionInfo();
                rptdb.ID = REPORTDB;
                rptdb.DisplayName = "FleetView Report";
                rptdb.DatabaseType = "MSSQL";
                rptdb.ConnectionString = rptstr;
                ls.Add(rptdb);
            }
            return ls.ToArray();
        }

        class FICCacheObject
        {
            public byte[] Data = null;
            public DateTime CreateTime = DateTime.Now;

            public byte[] ToBuffer()
            {
                byte[] rst = new byte[Data.Length + 8];
                byte[] bf1 = BitConverter.GetBytes(CreateTime.Ticks);
                Buffer.BlockCopy(bf1, 0, rst, 0, 8);
                Buffer.BlockCopy(Data, 0, rst, 8, Data.Length);

                return rst;
            }

            public void FromBuffer(byte[] buffer)
            {
                long l = BitConverter.ToInt64(buffer, 0);
                CreateTime = new DateTime(l);
                Data = new byte[buffer.Length - 8];
                Buffer.BlockCopy(buffer, 8, Data, 0, buffer.Length - 8);
            }
        }

        #region - (De)Serialize -

        private static byte[] Serialize(object obj, DateTime createtime)
        {
            if (obj == null)
            {
                return null;
            }
            var cacheObj = new RedisCacheObject
            {
                CreateTime = createtime,
                Data = obj
            };
            byte[] data;
            using (var ms = new MemoryStream())
            {
                new BinaryFormatter().Serialize(ms, cacheObj);
                data = ms.ToArray();
            }
            return data;
        }

        private static object Deserialize(byte[] buffer)
        {
            using (var ms = new MemoryStream(buffer, false))
            {
                return new BinaryFormatter().Deserialize(ms);
            }
        }

        public byte[] GetCompanyLogo()
        {
            return SystemParams.GetCompanyLOGO(SystemParams.CompanyID);
        }

        public AreaUnits GetAreaUnit()
        {
            var cust = SystemParams.CustomerDetail;
            if (cust != null)
            {
                return cust.AreaUnit;
            }
            else
            {
                return AreaUnits.Acre;
            }
        }

        public DistanceUnits GetDistanceUnit()
        {
            var cust = SystemParams.CustomerDetail;
            if (cust != null)
            {
                return cust.OdometerUnit;
            }
            else
            {
                return DistanceUnits.Kilometres;
            }
        }

        public SpeedUnits GetSpeedUnit()
        {
            DistanceUnits du = GetDistanceUnit();
            switch (du)
            {
                case DistanceUnits.Kilometres: return SpeedUnits.KPH;
                case DistanceUnits.Metres: return SpeedUnits.MetersPerSecond;
                case DistanceUnits.Miles: return SpeedUnits.MPH;
                default:
                    return SpeedUnits.KPH;
            }
        }

        public VolumnUnits GetVolumnUnit()
        {
            var cust = SystemParams.CustomerDetail;
            if (cust != null)
            {
                return cust.VolumnUnit;
            }
            else
            {
                return VolumnUnits.Litre;
            }
        }

        public WeightUnits GetWeightUnit()
        {
            var cust = SystemParams.CustomerDetail;
            if (cust != null)
            {
                return cust.WeightUnit;
            }
            else
            {
                return WeightUnits.Kilogram;
            }
        }

        public SpecialGlobalFilterInfo[] GetSpecialGlobalFilters()
        {
            return FICSpecialGolbalFilterManager.GlobalFilters;
        }

        public object[] GetSpecialGlobalFilterValuesByUser(string useriid, string filterid)
        {
            return FICSpecialGolbalFilterManager.GetSpecialGlobalFilterValuesByUser(useriid, filterid);
        }

        public FilterTemplateInfo[] GetFilterTemplates(string useriid)
        {
            return FICSpecialGolbalFilterManager.GetFilterTemplates(useriid);
        }

        public object[] GetFilterValues(string useriid, int filterid)
        {
            return FICSpecialGolbalFilterManager.GetFilterValues(useriid, filterid);
        }

        public bool CanExportToFile(string useriid)
        {
            var client = FleetServiceClientHelper.CreateClient<FleetUser.UserQueryClient>();
            var user = client.GetUserByIID(useriid);
            if (user.UserType < FleetUser.UserTypes.Admin)
            {
                var atta = client.GetUserAdditionalAttribute(useriid);
                if (atta != null)
                    return atta.ExcelExports;
            }
            else
                return true;
            return false;
        }

        public void WriteExportAuditTrail(string useriid, string doctype, string docid, string notes, string filename, string filetype, byte[] filedata)
        {
            var client = FleetServiceClientHelper.CreateClient<DocCommentProvider>();
            client.DocumentExportAuditTrail(SystemParams.CompanyID, useriid, doctype, docid, notes, filename, filetype, filedata);
        }

        private static bool IsValidPhoneNumber(string number)
        {
            if (string.IsNullOrWhiteSpace(number))
            {
                return false;
            }

            string s1 = number.Replace(" ", string.Empty)
                .Replace("(", string.Empty)
                .Replace(")", string.Empty)
                .Replace("-", string.Empty);

            if(string.IsNullOrWhiteSpace(s1))
            {
                return false;
            }

            foreach (char c in s1)
            {
                if (c < '0' || c > '9')
                {
                    return false;
                }
            }
            return true;
        }

        public long SendSMS(string source, string sourceid, string sendingnumber, string receiverphonenumber, string message, string sender)
        {
            if(!IsValidPhoneNumber(sendingnumber))
            {
                throw new Exception("Invalid phone number: " + sendingnumber);
            }
            if(!IsValidPhoneNumber(receiverphonenumber))
            {
                throw new Exception("Invalid phone number: " + receiverphonenumber);
            }
            var client = FleetServiceClientHelper.CreateClient<SystemUtil>();
            return client.SendSMS(SystemParams.CompanyID, source, sourceid, sendingnumber, receiverphonenumber, message, sender);
        }

        public int GetSMSStatus(long smsid)
        {
            var client = FleetServiceClientHelper.CreateClient<SystemUtil>();
            return client.GetSMSStatus(SystemParams.CompanyID, smsid);
        }

        public long SendEMail(MailMessage message)
        {
            return SystemParams.SendMail(message);
        }

        public int GetEmailStatus(long mailid)
        {
            var client = FleetServiceClientHelper.CreateClient<SystemUtil>();
            return client.GetEmailStatus(SystemParams.CompanyID, mailid);
        }



        #endregion


        [Serializable]
        public class RedisCacheObject
        {
            public DateTime CreateTime { get; set; }
            public object Data { get; set; }
        }

    }

}
