using Http.Core;
using Http.Core.Contexts;
using Http.Core.Exceptions;
using Http.Core.Extensions;
using Http.Core.Models;
using PCUT.Extensions;
using PCUT.Helpers;
using PCUT.Models;
using PCUT.Pages;
using System;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Windows.Input;
using Windows.Security.Credentials;
using Windows.Security.Cryptography;
using Windows.System.Profile;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using static Http.Core.Constants.HttpConstants;
using static Http.Core.Extensions.HttpExtensions;

namespace PCUT.ViewModels
{
    public class LoginViewModel : NotificationBase
    {
        private const string REMEMBER_ME_KEY = "RememberMe";
        private const string REMEMBER_USER_KEY = "RememberMeUser";
        private const string DEVICE_ID_KEY = "DeviceId";

        private bool _rememberMe;
        public bool RememberMe
        {
            get { return _rememberMe; }
            set
            {
                SetProperty(ref _rememberMe, value);
                var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
                localSettings.Values[REMEMBER_ME_KEY] = value;
            }
        }

        private string _username;
        public string Username
        {
            get { return _username; }
            set
            {
                SetProperty(ref _username, value);
                LoginEnabled = CheckLoginEnabled();
            }
        }

        private string _password;
        public string Password
        {
            get { return _password; }
            set
            {
                SetProperty(ref _password, value);
                LoginEnabled = CheckLoginEnabled();
            }
        }

        private bool _loginEnabled = false;
        public bool LoginEnabled
        {
            get { return _loginEnabled; }
            private set
            {
                if (_loginEnabled != value)
                    SetProperty(ref _loginEnabled, value);
            }
        }

        public string VersionDisplay = $"Version {UserContext.Instance.AppVersion}";

        public ICommand NavigateMainCommand { get; private set; }

        private bool _success = false;
        private async Task LoginAsync()
        {
            LoginEnabled = false;
            try
            {
                var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
                localSettings.Values.Remove(REMEMBER_USER_KEY);

                bool loginSuccess = false;
                string errorMessage = null;
                using (var client = HttpClientFactory.CreateClient(ClientNames.AuthClient))
                {
                    try
                    {
                        var loginResult = await client.LoginAsync(new LoginRequest(Username, Password), Username);
                        loginSuccess = loginResult.Success;
                        if (!loginSuccess)
                            errorMessage = loginResult.Message;
                    }
                    catch (Exception ex) when (!(ex is AppOutdatedException))
                    {
                        errorMessage = ex.Message;
                    }
                }

                if (!loginSuccess)
                {
                    await new MessageDialog(errorMessage).ShowAsync();
                    return;
                }

                using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient))
                {
                    try
                    {
                        await client.LoadUserProfileAsync();
                    }
                    catch (Exception ex) when (!(ex is AppOutdatedException)) { }
                }

                if (RememberMe)
                {
                    localSettings.Values[REMEMBER_USER_KEY] = Username;
                    UpsertRememberMe(Username, Password);
                }

                _success = true;
                await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    ((Frame)Window.Current.Content).Navigate(typeof(MainMenuPage));
                });
            }
            finally
            {
                if (!_success)
                    LoginEnabled = CheckLoginEnabled();
            }
        }

        public LoginViewModel()
        {
            var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
            if (localSettings.TryGetValue<bool>(REMEMBER_ME_KEY, out var rememberMe) && rememberMe)
            {
                RememberMe = true;
                if (localSettings.TryGetValue<string>(REMEMBER_USER_KEY, out var userName))
                    (Username, Password) = LoadRememberMe(userName);
            }
            if (!localSettings.TryGetValue<string>(DEVICE_ID_KEY, out var deviceId) || string.IsNullOrEmpty(deviceId))
            {
                var sysInfo = SystemIdentification.GetSystemIdForPublisher();
                if (sysInfo != null)
                {
                    deviceId = CryptographicBuffer.EncodeToBase64String(sysInfo.Id);
                }
                else
                {
                    // random int64 number
                    var salt = new byte[8];
                    RandomNumberGenerator.Create().GetNonZeroBytes(salt);
                    // random guid
                    var uuid = Guid.NewGuid().ToByteArray();
                    // random timestamp
                    var timestamp = BitConverter.GetBytes(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());

                    var data = new byte[uuid.Length + timestamp.Length + salt.Length];
                    Array.Copy(salt, 0, data, 0, salt.Length);
                    Array.Copy(uuid, 0, data, 0 + salt.Length, uuid.Length);
                    Array.Copy(timestamp, 0, data, 0 + salt.Length + uuid.Length, timestamp.Length);

                    deviceId = Convert.ToBase64String(data);
                }
                localSettings.Values[DEVICE_ID_KEY] = deviceId;
            }
            UserContext.Instance.SetDeviceId(deviceId);
            NavigateMainCommand = new RelayCommand(LoginAsync, () => !_success);
        }

        private bool CheckLoginEnabled()
        {
            return !string.IsNullOrWhiteSpace(_username) && !string.IsNullOrWhiteSpace(_password);
        }

        private (string UserName, string Password) LoadRememberMe(string userName)
        {
            if (string.IsNullOrWhiteSpace(userName))
                return default;

            var vault = new PasswordVault();
            try
            {
                var credential = vault.Retrieve(REMEMBER_ME_KEY, userName);
                return (credential?.UserName, credential?.Password);
            }
            catch
            {
                return default;
            }
        }

        private void UpsertRememberMe(string userName, string password)
        {
            var vault = new PasswordVault();
            PasswordCredential credential;
            try
            {
                credential = vault.Retrieve(REMEMBER_ME_KEY, userName);
                credential.Password = password;
            }
            catch
            {
                credential = new PasswordCredential(REMEMBER_ME_KEY, userName, password);
            }
            vault.Add(credential);
        }
    }
}
