using Borepin.Service.Storage.Exceptions;
using FabAccessAPI;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Borepin.Service.Storage
{
    public class LoginStorageService : ILoginStorageService
    {
        #region Static Members
        const string StorageKey_ConnectionDataList = "ConnectionData";
        const string StorageKey_ConnecitonData_AutoConnect = "ConnectionData_Default";
        #endregion

        #region Private Members
        private readonly ISecretStorageService _SecretStorageService;
        #endregion

        #region Constructors
        public LoginStorageService(ISecretStorageService secretStorageService)
        {
            _SecretStorageService = secretStorageService;
        }
        #endregion

        #region Methods
        public async Task<IList<ConnectionData>> GetList()
        {
            return await _LoadConnectionData().ConfigureAwait(false);
        }

        public async Task Add(ConnectionData connectionData)
        {
            if (connectionData.Host.Host == string.Empty || connectionData.Username == string.Empty)
            {
                throw new InvalidConnectionExceptoin(connectionData);
            }

            IList<ConnectionData> connectionData_List = await _LoadConnectionData().ConfigureAwait(false);
            if(connectionData_List.Contains(connectionData))
            {
                connectionData_List.Remove(connectionData);
            }

            connectionData_List.Add(connectionData);
            await _SaveConnectionData(connectionData_List).ConfigureAwait(false);
        }
        public async Task Remove(ConnectionData connectionData)
        {
            if (connectionData.Host.Host == string.Empty || connectionData.Username == string.Empty)
            {
                throw new InvalidConnectionExceptoin(connectionData);
            }

            List<ConnectionData> connectionData_List = new List<ConnectionData>(await _LoadConnectionData().ConfigureAwait(false));
            if (!connectionData_List.Contains(connectionData))
            {
                throw new MissingConnectionException(connectionData);
            }

            connectionData_List.Remove(connectionData);
            await _SaveConnectionData(connectionData_List).ConfigureAwait(false);

            ConnectionData connectionData_default = await GetDefault().ConfigureAwait(false);
            if (connectionData_default != null && connectionData_default.Equals(connectionData))
            {
                await RemoveDefault().ConfigureAwait(false);
            }
        }

        public async Task UpdateTimestamp(ConnectionData connectionData)
        {
            if (connectionData.Host.Host == string.Empty || connectionData.Username == string.Empty)
            {
                throw new InvalidConnectionExceptoin(connectionData);
            }

            List<ConnectionData> connectionData_List = new List<ConnectionData>(await _LoadConnectionData().ConfigureAwait(false));
            if (! connectionData_List.Contains(connectionData))
            {
                throw new MissingConnectionException(connectionData);
            }

            connectionData.LastTime = DateTime.UtcNow;

            connectionData_List.Remove(connectionData);
            connectionData_List.Add(connectionData);
            await _SaveConnectionData(connectionData_List).ConfigureAwait(false);
        }

        public async Task<ConnectionData> GetDefault()
        {
            try
            {
                string data = await _SecretStorageService.GetAsync(StorageKey_ConnecitonData_AutoConnect).ConfigureAwait(false);
                if (data != null)
                {
                    ConnectionData connectionData = JsonConvert.DeserializeObject<ConnectionData>(data);
                    return connectionData;
                }
                return null;
            }
            catch (JsonSerializationException)
            {
                _SecretStorageService.Remove(StorageKey_ConnecitonData_AutoConnect);
                return null;
            }
        }

        public async Task SetDefault(ConnectionData connectionData)
        {
            string data = JsonConvert.SerializeObject(connectionData);
            await _SecretStorageService.SetAsync(StorageKey_ConnecitonData_AutoConnect, data).ConfigureAwait(false);
        }

        public async Task RemoveDefault()
        {
            string data = JsonConvert.SerializeObject(null);
            await _SecretStorageService.SetAsync(StorageKey_ConnecitonData_AutoConnect, data).ConfigureAwait(false);
        }
        #endregion

        #region Private Methods
        private async Task<IList<ConnectionData>> _LoadConnectionData()
        {
            List<ConnectionData> connectionData_List;
            try
            {
                string data = await _SecretStorageService.GetAsync(StorageKey_ConnectionDataList).ConfigureAwait(false);
                if(data != null)
                {
                    connectionData_List = JsonConvert.DeserializeObject<List<ConnectionData>>(data);
                }
                else
                {
                    connectionData_List = new List<ConnectionData>();
                    await _SecretStorageService.SetAsync(StorageKey_ConnectionDataList, JsonConvert.SerializeObject(connectionData_List)).ConfigureAwait(false);
                }
            }
            catch (JsonSerializationException)
            {
                connectionData_List = new List<ConnectionData>();
                await _SecretStorageService.SetAsync(StorageKey_ConnectionDataList, JsonConvert.SerializeObject(connectionData_List)).ConfigureAwait(false);
            }

            return connectionData_List;
        }

        private async Task _SaveConnectionData(IList<ConnectionData> connectionData_List)
        {
            string data = JsonConvert.SerializeObject(connectionData_List);
            await _SecretStorageService.SetAsync(StorageKey_ConnectionDataList, data).ConfigureAwait(false);
        }
        #endregion
    }
}
