using System;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;

using Xamarin.Forms;

using RestSharp;
using Plugin.Permissions;
using Plugin.Permissions.Abstractions;

using Syncfusion.Licensing;
using comal.timesheets;
using Xamarin.Essentials;
using PermissionStatus = Plugin.Permissions.Abstractions.PermissionStatus;
using InABox.Core;
using InABox.Clients;
using InABox.Configuration;
using Comal.Classes;

namespace InABox.Mobile
{

    public enum PermissionType
    {

        Unknown,
        Calendar,
        Camera,
        Contacts,
        Location,
        Microphone,
        Phone,
        Photos,
        Reminders,
        Sensors,
        Sms,
        Storage,
        Speech,
        LocationAlways,
        LocationWhenInUse,
        MediaLibrary
    }



    public static class MobileUtils
    {

        public static void Init()
        {
            Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense(InABox.Core.CoreUtils.SyncfusionLicense(Core.SyncfusionVersion.v20_3));
        }

        public static String GetDeviceID()
        {
            IDeviceID deviceID = DependencyService.Get<IDeviceID>();
            return deviceID.DeviceID();
        }

        public static IAppVersion AppVersion { get { return DependencyService.Get<IAppVersion>(); } }

        public static void SaveToSecureStorage()
        {
            try
            {
                if (!string.IsNullOrWhiteSpace(App.Settings.UserID))
                    SecureStorage.SetAsync(GlobalVariables.CacheUserIDString, App.Settings.UserID);

                if (!string.IsNullOrWhiteSpace(App.Settings.Password))
                    SecureStorage.SetAsync(GlobalVariables.CachePasswordString, App.Settings.Password);

                if (App.DBSettings.URLs.Count() > 0)
                {
                    int count = 0;
                    foreach (string url in App.DBSettings.URLs)
                    {
                        SecureStorage.SetAsync(GlobalVariables.CacheSettingsURL + count, url);
                        count++;
                    }
                }
            }
            catch { }
        }

        public static void LoadFromLink()
        {
            try
            {
                var arr = GetArray();
                var array = arr.Item1;
                var s = arr.Item2;
                if (!CheckExpiry(array))
                {
                    GlobalVariables.LoadFromLinkString = "Link has expired";
                    return;
                }

                SetDBSettings(array, s);
                SaveSettings();
                RemoveSessionID();
                PingAndSetClient();
            }
            catch (Exception e)
            {
                if (String.IsNullOrWhiteSpace(App.DBSettings.URLs[0]))
                    LoadDemoSettings(App.DBSettings);
            }
        }

        private static void PingAndSetClient()
        {
            var result = RestClient<User>.Ping(App.DBSettings.URLs, out DatabaseInfo info);

            ClientFactory.SetClientType(typeof(RestClient<>), InABox.Core.Platform.Mobile, MobileUtils.AppVersion.InstalledVersionNumber + App.DeviceString, result, true);
        }

        private static Tuple<string[], string> GetArray()
        {
            var s = Encryption.Decrypt(GlobalVariables.LoadFromLinkString, "logindetailslink");
            var index = s.IndexOf("ENDURLS");
            var substring = s.Substring(index + 8, s.Length - index - 8);
            return new Tuple<string[], string>(substring.Split(","), s);
        }

        private static bool CheckExpiry(string[] array)
        {
            DateTime expiry = DateTime.Parse(array[2]);
            if (DateTime.Now.Subtract(expiry) < new TimeSpan(0, 5, 0))
                return true;
            else
                return false;
        }

        private static void SetDBSettings(string[] array, string s)
        {
            var urls = s.Substring(0, s.IndexOf("ENDURLS") - 1);
            App.DBSettings.URLs = urls.Split(",");
            App.DBSettings.UserID = array[0];
            App.DBSettings.Password = array[1];
        }

        private static void SaveSettings()
        {
            ClientFactory.InvalidateUser();
            new LocalConfiguration<DatabaseSettings>().Delete();
            new LocalConfiguration<DatabaseSettings>().Save(App.DBSettings);
            App.DBSettings = new LocalConfiguration<DatabaseSettings>().Load();
            GlobalVariables.ChangeUser = true;

            SaveToSecureStorage();
        }

        private static void RemoveSessionID()
        {
            if (App.Current.Properties.ContainsKey("SessionID"))
                App.Current.Properties.Remove("SessionID");
        }

        public static void LoadDemoSettings(DatabaseSettings settings)
        {
            List<string> list = new List<string>();
            list.Add("http://demo.prsdigital.com.au:8003");
            list.Add("http://demo2.prsdigital.com.au:8003");
            list.Add("http://demo3.prsdigital.com.au:8003");
            settings.URLs = list.ToArray();
            settings.UserID = "GUEST";
            settings.Password = "guest";
        }


        private static async Task<bool> Retry(Func<Task<bool>> action, int interval, int retryCount = 3)
        {
            bool flag = false;
            List<Exception> source = new List<Exception>();
            TimeSpan delay = TimeSpan.FromMilliseconds((double)interval);
            for (int index = 0; index < retryCount; ++index)
            {
                try
                {
                    flag = await action();
                    if (flag)
                        return true;
                    Task.Delay(delay).Wait();
                }
                catch (Exception ex)
                {
                    if (source.All<Exception>((Func<Exception, bool>)(x => x.Message != ex.Message)))
                        source.Add(ex);
                    Task.Delay(delay).Wait();
                }
            }
            if (!flag && !source.Any<Exception>())
                return false;
            if (source.Count<Exception>() == 1)
                throw source[0];
            if (source.Count<Exception>() > 1)
                throw new AggregateException((IEnumerable<Exception>)source);
            return flag;
        }


        public static async Task<bool> IsPermitted(PermissionType permissiontype)
        {
            try
            {
                Permission permission = (Permission)permissiontype;

                PermissionStatus status = await CrossPermissions.Current.CheckPermissionStatusAsync(permission);
                if (status == PermissionStatus.Granted)
                    return true;
                Dictionary<Permission, PermissionStatus> statuses = await CrossPermissions.Current.RequestPermissionsAsync(permission);
                if (statuses[permission] == PermissionStatus.Granted)
                    return true;
                return false;
            }
            catch (System.Threading.Tasks.TaskCanceledException ex)
            {
                return false;
            }

            //request.Wait();
            //var statuses = request.Result;
            //return (statuses.ContainsKey(permission) && (statuses[permission] == PermissionStatus.Granted));

            //PermissionStatus status = PermissionStatus.Unknown;

            //var result = Retry(async () =>
            //{
            //    status = await CrossPermissions.Current.CheckPermissionStatusAsync(permission);
            //    if (status != PermissionStatus.Granted)
            //    {
            //        var results = await CrossPermissions.Current.RequestPermissionsAsync(permission);
            //        if (results.ContainsKey(permission))
            //            status = results[permission];
            //    }

            //    return status == PermissionStatus.Granted;

            //}, 5000, 3);

            //result.Wait();

            //return result.Result;
        }


        public static async Task<bool> CheckPermissions(PermissionType permissiontype)
        {
            Permission permission = (Permission)permissiontype;
            var permissionStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(permission);
            bool request = false;
            if (permissionStatus == PermissionStatus.Denied)
            {
                if (String.Equals(Device.RuntimePlatform, Device.iOS))
                {

                    var title = $"{permission} Permission";
                    var question = $"To use this plugin the {permission} permission is required. Please go into Settings and turn on {permission} for the app.";
                    var positive = "Settings";
                    var negative = "Maybe Later";
                    var task = Application.Current?.MainPage?.DisplayAlert(title, question, positive, negative);
                    if (task == null)
                        return false;

                    var result = await task;
                    if (result)
                    {
                        CrossPermissions.Current.OpenAppSettings();
                    }

                    return false;
                }

                request = true;

            }

            if (request || permissionStatus != PermissionStatus.Granted)
            {
                var newStatus = await CrossPermissions.Current.RequestPermissionsAsync(permission);
                if (newStatus.ContainsKey(permission) && newStatus[permission] != PermissionStatus.Granted)
                {
                    var title = $"{permission} Permission";
                    var question = $"To use the plugin the {permission} permission is required.";
                    var positive = "Settings";
                    var negative = "Maybe Later";
                    var task = Application.Current?.MainPage?.DisplayAlert(title, question, positive, negative);
                    if (task == null)
                        return false;

                    var result = await task;
                    if (result)
                    {
                        CrossPermissions.Current.OpenAppSettings();
                    }
                    return false;
                }
            }

            return true;
        }

    }
}
