<commit_before>﻿using Akavache;
using Ets.Mobile.Client;
using Ets.Mobile.Client.Contracts;
using Ets.Mobile.Client.Mixins;
using Ets.Mobile.Entities.Auth;
using Ets.Mobile.ViewModel.Bases;
using Ets.Mobile.ViewModel.Contracts.Account;
using Ets.Mobile.ViewModel.Pages.Main;
using Logger;
using Messaging.UniversalApp.Common;
using ReactiveUI;
using Refit;
using Security.Contracts;
using Splat;
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Reactive.Threading.Tasks;
using System.Runtime.Serialization;
using System.Threading.Tasks;

namespace Ets.Mobile.ViewModel.Pages.Account
{
    [DataContract]
    public class LoginPageViewModel : ViewModelBase, ILoginPageViewModel
    {
        /// <summary>
        /// Constructor LoginPageViewModel
        /// </summary>
        public LoginPageViewModel(IScreen screen) : base(screen, "Login")
        {
            OnViewModelCreation();
        }

        protected sealed override void OnViewModelCreation()
        {
            UserName = string.Empty;
            Password = string.Empty;
            LogSubject = new ReplaySubject<string>(1);
            LogSubject.Subscribe(message => this.Log().Info(message));

            _isValidating = false;

            // Can Login Execute
            var userNameChanged = this.WhenAny(vm => vm.UserName, x => !string.IsNullOrEmpty(x.Value));
            var passwordChanged = this.WhenAny(vm => vm.Password, changed => !string.IsNullOrWhiteSpace(changed.Value));
            var isValidatingChanged = this.WhenAny(vm => vm._isValidating, changed => !changed.Value);
            var canLoginExecute = passwordChanged.CombineLatest(userNameChanged, isValidatingChanged, 
                (validPass, validUserName, isNotValidating) => validPass & validUserName & isNotValidating
            );

            Login = ReactiveCommand.CreateAsyncTask(canLoginExecute, async _ => await LoginImpl());

            Login.Subscribe(accountVm => {
                LogSubject.OnNext("Navigate to MainPageViewModel");
                HostScreen.Router.NavigateAndReset.Execute(new MainPageViewModel(HostScreen));
            });

            Login.ThrownExceptions.Subscribe(LoginThrownExceptionImpl);
        }

        private async Task<EtsUserCredentials> LoginImpl()
        {
            _isValidating = true;

            LogSubject.OnNext("Start Loging In");
            LogSubject.OnNext($"Send Request to Login for {UserName}");

            var checkCredentialsTask = ClientServices().SignetsService.Login(UserName, Password);
            // The Login sometimes takes way too long to return a value (more than 5 minutes sometimes, when other times it takes under a second)
            var fetchLogin = await Task.WhenAny(checkCredentialsTask, Task.Delay(LoginTimeoutMs));
            if (fetchLogin != checkCredentialsTask)
            {
                // Operation has timed out
                throw new SignetsException(Resources().GetStringForKey("LoginTimeoutMessage"));
            }

            LogSubject.OnNext(checkCredentialsTask.Result ? "Authentificated sucessfully" : "Invalid credentials");

            if (!checkCredentialsTask.Result)
            {
                // invalid credentials
                throw new SignetsException(Resources().GetStringForKey("LoginInvalidCredentialsMessage"));
            }

            LogSubject.OnNext("Storing your credentials securely");
            var credentials = new EtsUserCredentials(UserName, Password);
            await Cache.InsertObject(ViewModelKeys.Login, credentials).ToTask();

            LogSubject.OnNext("Set credentials in the services");
            Locator.Current.GetService<ISsoService>().SetCredentials(credentials);

            LogSubject.OnNext("Save the credentials for logging");
            Locator.Current.GetService<IUserEnabledLogger>().SetUser(Locator.Current.GetService<ISecurityProvider>().HashMd5(credentials.Username));

            LogSubject.OnNext("Load details about the logged user");
            SideNavigation.UserDetails.LoadProfile.Execute(null);

            LogSubject.OnNext("Preload courses to have the colors ready on all pages");
            var coursesTask = await ClientServices().SignetsService.Courses().ApplyCustomColors(SettingsService());
            await Cache.InsertObject(ViewModelKeys.Courses, coursesTask).ToTask();

            LogSubject.OnNext("Retrieving schedule");
            var semesters = await ClientServices().SignetsService.Semesters();
            await Cache.InsertObject(ViewModelKeys.Semesters, semesters).ToTask();
            var currentSemester = semesters.FirstOrDefault(y => y.StartDate <= DateTime.Now && y.EndDate > DateTime.Now);
            if (currentSemester != null)
            {
                var schedule = await ClientServices().SignetsService.Schedule(currentSemester.AbridgedName).ApplyCustomColors(SettingsService());

                await Cache.InsertObject(ViewModelKeys.ScheduleForSemester(currentSemester.AbridgedName), schedule).ToTask();
                LogSubject.OnNext("Saved the schedule");
            }

            LogSubject.OnNext("Register Schedule Tile and LockScreen Updater");
            await Agent.ScheduleTileUpdaterBackgroundTask.Register();
            await Cache.InsertObject(ViewModelKeys.ScheduleTileUpdaterActive, true).ToTask();

            LogSubject.OnNext("Completed login flow");
            
            _isValidating = false;

            return credentials;
        }

        private void LoginThrownExceptionImpl(Exception ex)
        {
            var apiException = ex as ApiException;
            var agregateException = ex as AggregateException;
            var isNetworkExceptionRelated = apiException != null || agregateException?.InnerExceptions?.First() is ApiException;

            var messageToDisplay = isNetworkExceptionRelated ? new ErrorMessageContent(Resources().GetStringForKey("NetworkError"), Resources().GetStringForKey("NetworkTitleError"), apiException) : new ErrorMessageContent(ex.Message, ex);

            if (apiException == null)
            {<commit_msg>When in internship, we still want the user to access the app.<commit_after>﻿using Akavache;
using Ets.Mobile.Client;
using Ets.Mobile.Client.Contracts;
using Ets.Mobile.Client.Mixins;
using Ets.Mobile.Entities.Auth;
using Ets.Mobile.ViewModel.Bases;
using Ets.Mobile.ViewModel.Contracts.Account;
using Ets.Mobile.ViewModel.Pages.Main;
using Logger;
using Messaging.UniversalApp.Common;
using ReactiveUI;
using Refit;
using Security.Contracts;
using Splat;
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Reactive.Threading.Tasks;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Ets.Mobile.Entities.Signets;

namespace Ets.Mobile.ViewModel.Pages.Account
{
    [DataContract]
    public class LoginPageViewModel : ViewModelBase, ILoginPageViewModel
    {
        /// <summary>
        /// Constructor LoginPageViewModel
        /// </summary>
        public LoginPageViewModel(IScreen screen) : base(screen, "Login")
        {
            OnViewModelCreation();
        }

        protected sealed override void OnViewModelCreation()
        {
            UserName = string.Empty;
            Password = string.Empty;
            LogSubject = new ReplaySubject<string>(1);
            LogSubject.Subscribe(message => this.Log().Info(message));

            _isValidating = false;

            // Can Login Execute
            var userNameChanged = this.WhenAny(vm => vm.UserName, x => !string.IsNullOrEmpty(x.Value));
            var passwordChanged = this.WhenAny(vm => vm.Password, changed => !string.IsNullOrWhiteSpace(changed.Value));
            var isValidatingChanged = this.WhenAny(vm => vm._isValidating, changed => !changed.Value);
            var canLoginExecute = passwordChanged.CombineLatest(userNameChanged, isValidatingChanged,
                (validPass, validUserName, isNotValidating) => validPass & validUserName & isNotValidating
            );

            Login = ReactiveCommand.CreateAsyncTask(canLoginExecute, async _ => await LoginImpl());

            Login.Subscribe(accountVm =>
            {
                LogSubject.OnNext("Navigate to MainPageViewModel");
                HostScreen.Router.NavigateAndReset.Execute(new MainPageViewModel(HostScreen));
            });

            Login.ThrownExceptions.Subscribe(LoginThrownExceptionImpl);
        }

        private async Task<EtsUserCredentials> LoginImpl()
        {
            _isValidating = true;

            LogSubject.OnNext("Start Loging In");
            LogSubject.OnNext($"Send Request to Login for {UserName}");

            var checkCredentialsTask = ClientServices().SignetsService.Login(UserName, Password);
            // The Login sometimes takes way too long to return a value (more than 5 minutes sometimes, when other times it takes under a second)
            var fetchLogin = await Task.WhenAny(checkCredentialsTask, Task.Delay(LoginTimeoutMs));
            if (fetchLogin != checkCredentialsTask)
            {
                // Operation has timed out
                throw new SignetsException(Resources().GetStringForKey("LoginTimeoutMessage"));
            }

            LogSubject.OnNext(checkCredentialsTask.Result ? "Authentificated sucessfully" : "Invalid credentials");

            if (!checkCredentialsTask.Result)
            {
                // invalid credentials
                throw new SignetsException(Resources().GetStringForKey("LoginInvalidCredentialsMessage"));
            }

            LogSubject.OnNext("Storing your credentials securely");
            var credentials = new EtsUserCredentials(UserName, Password);
            await Cache.InsertObject(ViewModelKeys.Login, credentials).ToTask();

            LogSubject.OnNext("Set credentials in the services");
            Locator.Current.GetService<ISsoService>().SetCredentials(credentials);

            LogSubject.OnNext("Save the credentials for logging");
            Locator.Current.GetService<IUserEnabledLogger>()
                .SetUser(Locator.Current.GetService<ISecurityProvider>().HashMd5(credentials.Username));

            LogSubject.OnNext("Load details about the logged user");
            SideNavigation.UserDetails.LoadProfile.Execute(null);

            LogSubject.OnNext("Preload courses to have the colors ready on all pages");
            var coursesTask = await ClientServices().SignetsService.Courses().ApplyCustomColors(SettingsService());
            await Cache.InsertObject(ViewModelKeys.Courses, coursesTask).ToTask();

            LogSubject.OnNext("Retrieving schedule");
            var semesters = await ClientServices().SignetsService.Semesters();
            await Cache.InsertObject(ViewModelKeys.Semesters, semesters).ToTask();
            var currentSemester = semesters.FirstOrDefault(y => y.StartDate <= DateTime.Now && y.EndDate > DateTime.Now);
            if (currentSemester != null)
            {
                var schedule = new ScheduleVm[0];
                try
                {
                    schedule = await ClientServices().SignetsService.Schedule(currentSemester.AbridgedName).ApplyCustomColors(SettingsService());

                    LogSubject.OnNext("Saved the schedule");
                }
                catch (Exception) // Not having classes is no big deal.
                {
                    LogSubject.OnNext("There was no classes or another error occured");
                }
                finally
                {
                    await Cache.InsertObject(ViewModelKeys.ScheduleForSemester(currentSemester.AbridgedName), schedule).ToTask();
                }
            }

            LogSubject.OnNext("Register Schedule Tile and LockScreen Updater");
            await Agent.ScheduleTileUpdaterBackgroundTask.Register();
            await Cache.InsertObject(ViewModelKeys.ScheduleTileUpdaterActive, true).ToTask();

            LogSubject.OnNext("Completed login flow");
            
            _isValidating = false;

            return credentials;
        }

        private void LoginThrownExceptionImpl(Exception ex)
        {
            var apiException = ex as ApiException;
            var agregateException = ex as AggregateException;
            var isNetworkExceptionRelated = apiException != null || agregateException?.InnerExceptions?.First() is ApiException;

            var messageToDisplay = isNetworkExceptionRelated ? new ErrorMessageContent(Resources().GetStringForKey("NetworkError"), Resources().GetStringForKey("NetworkTitleError"), apiException) : new ErrorMessageContent(ex.Message, ex);

            if (apiException == null)
            {
{'fraction_non_alphanumeric': 0.09273026058377025, 'fraction_numerical': 0.0005477736912121449, 'mean_word_length': 2.7022016222479723, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '418372', 'n_tokens_mistral': 3173, 'n_tokens_neox': 3103, 'n_words': 672}