using Borepin.Base;
using Borepin.Base.Exceptions;
using Borepin.Service;
using Borepin.Service.ErrorMessage;
using Borepin.Service.Storage;
using FabAccessAPI;
using FabAccessAPI.Exceptions;
using FabAccessAPI.Exceptions.SASL;
using Prism.Commands;
using Prism.Navigation;
using Prism.Services;
using Prism.Services.Dialogs;
using System;
using System.Globalization;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;

namespace Borepin.PageModel
{
    public class ServerPageModel : ConnectionModelBase
    {
        #region Private Fields
        private readonly IDialogService _DialogService;
        private readonly ILoginStorageService _LoginStorageService;
        private readonly IErrorMessageService _ErrorMessageService;
        private bool IsDialog = false;
        #endregion

        #region Constructors
        public ServerPageModel(INavigationService navigationService, IPageDialogService pageDialogService, IAPIService apiService, IDialogService dialogService, ILoginStorageService loginStorageService, IErrorMessageService errorMessageService) : base(navigationService, pageDialogService, apiService)
        {
            _DialogService = dialogService;
            _LoginStorageService = loginStorageService;
            _ErrorMessageService = errorMessageService;

            ConnectCommand = new DelegateCommand(async () => await ConnectCommandExecute().ConfigureAwait(false));
            DisconnectCommand = new DelegateCommand(async () => await DisonnectCommandExecute().ConfigureAwait(false));
            DeleteCommand = new DelegateCommand(DeleteCommandExecute);
            SetDefaultCommand = new DelegateCommand(async () => await SetDefaultCommandExecute().ConfigureAwait(false));
        }
        #endregion

        #region Data
        public override async Task LoadInstance(object instance)
        {
            if(instance != null)
            {
                if(instance is ConnectionData)
                {
                    Connection_Item = instance as ConnectionData;
                }
                else
                {
                    throw new InstanceIncorrectException();
                }

                if((_API.IsConnected || _API.IsConnecting) && Connection_Item != null)
                {
                    InstanceIsActiveConnection = Connection_Item.Equals(_API.ConnectionData);
                }
                else
                {
                    InstanceIsActiveConnection = false;
                }

                if(_Connection_Item != null && _Connection_Item.Host != null)
                {
                    DisplayAddress = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", _Connection_Item.Host.Host, _Connection_Item.Host.Port);
                }

                ConnectionData connectionData_Default = await _LoginStorageService.GetDefault().ConfigureAwait(false);
                if(connectionData_Default != null && connectionData_Default.Equals(_Connection_Item))
                {
                    InstanceIsDefaultConnection = true;
                }
            }
            if(instance == null && Connection_Item == null && IsDialog == false)
            {
                throw new InstanceIncorrectException();
            }

            IsBusy = false;
        }
        #endregion

        #region Fields
        private ConnectionData _Connection_Item;
        public ConnectionData Connection_Item
        {
            get => _Connection_Item;
            set => SetProperty(ref _Connection_Item, value);
        }

        private string _DisplayAddress;
        public string DisplayAddress
        {
            get => _DisplayAddress;
            set => SetProperty(ref _DisplayAddress, value);
        }

        private bool _InstanceIsActiveConnection;
        public bool InstanceIsActiveConnection
        {
            get => _InstanceIsActiveConnection;
            set => SetProperty(ref _InstanceIsActiveConnection, value);
        }

        private bool _InstanceIsDefaultConnection;
        public bool InstanceIsDefaultConnection
        {
            get => _InstanceIsDefaultConnection;
            set => SetProperty(ref _InstanceIsDefaultConnection, value);
        }
        #endregion

        #region Commands
        private ICommand _ConnectCommand;
        public ICommand ConnectCommand
        {
            get => _ConnectCommand;
            set => SetProperty(ref _ConnectCommand, value);
        }
        public async Task ConnectCommandExecute()
        {
            IsBusy = true;

            if(_API.IsConnected)
            {
                await _API.Disconnect().ConfigureAwait(true);
            }

            try
            {
                await _API.Connect(Connection_Item).ConfigureAwait(false);
            }
            catch(Exception exception)
            {
                _ErrorMessageService.DisplayConnectFailedMessage(exception);
                IsBusy = false;
                return;
            }

            await _LoginStorageService.UpdateTimestamp(_Connection_Item).ConfigureAwait(false);

            Device.BeginInvokeOnMainThread(async () =>
            {
                INavigationResult result = await _NavigationService.NavigateAsync("/MainPage/NavigationPage/MachineListPage").ConfigureAwait(false);
                if (result.Exception != null)
                {
                    Log.Fatal(result.Exception, "Navigating failed");
                }
            });
        }
        private ICommand _SetDefaultCommand;
        public ICommand SetDefaultCommand
        {
            get => _SetDefaultCommand;
            set => SetProperty(ref _SetDefaultCommand, value);
        }
        public async Task SetDefaultCommandExecute()
        {
            await _LoginStorageService.SetDefault(Connection_Item).ConfigureAwait(false);

            await LoadInstance(Connection_Item).ConfigureAwait(false);
        }

        private ICommand _DisconnectCommand;
        public ICommand DisconnectCommand
        {
            get => _DisconnectCommand;
            set => SetProperty(ref _DisconnectCommand, value);
        }
        public async Task DisonnectCommandExecute()
        {
            _API.UnbindEventHandler();
            await _API.Disconnect().ConfigureAwait(false);

            await LoadInstance(Connection_Item).ConfigureAwait(false);
        }

        private ICommand _DeleteCommand;
        public ICommand DeleteCommand
        {
            get => _DeleteCommand;
            set => SetProperty(ref _DeleteCommand, value);
        }

        public void DeleteCommandExecute()
        {
            DialogParameters parameters = new DialogParameters
            {
                { "title", Resources.Text.TextResource.DIALOG_DeleteServer },
                { "message", Resources.Text.TextResource.DIALOG_DeleteServerConfirm },
                { "instance", Connection_Item },
            };

            IsDialog = true;
            _DialogService.ShowDialog("ConfirmDialog", parameters, DeleteCommandExecute_Dialog);
        }

        public async void DeleteCommandExecute_Dialog(IDialogResult result)
        {
            if(string.Equals(result.Parameters.GetValue<string>("result"), "confirm", StringComparison.Ordinal))
            {
                await _API.Disconnect().ConfigureAwait(false);
                _API.UnbindEventHandler();
                await _LoginStorageService.Remove(result.Parameters.GetValue<ConnectionData>("instance")).ConfigureAwait(false);

                Device.BeginInvokeOnMainThread(async () =>
                {
                    INavigationResult result_nav = await _NavigationService.NavigateAsync("/MainPage/NavigationPage/ServerListPage").ConfigureAwait(false);
                    if (result_nav.Exception != null)
                    {
                        Log.Fatal(result.Exception, "Navigating failed");
                    }
                });
            }
        }
        #endregion
    }
}
