/*
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.gnd;

import androidx.core.view.WindowInsetsCompat;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.google.android.gnd.model.Project;
import com.google.android.gnd.repository.FeatureRepository;
import com.google.android.gnd.repository.ProjectRepository;
import com.google.android.gnd.repository.UserRepository;
import com.google.android.gnd.rx.Loadable;
import com.google.android.gnd.system.auth.AuthenticationManager;
import com.google.android.gnd.system.auth.SignInState;
import com.google.android.gnd.ui.common.AbstractViewModel;
import com.google.android.gnd.ui.common.EphemeralPopups;
import com.google.android.gnd.ui.common.Navigator;
import com.google.android.gnd.ui.common.SharedViewModel;
import com.google.android.gnd.ui.home.HomeScreenFragmentDirections;
import com.google.android.gnd.ui.signin.SignInFragmentDirections;
import io.reactivex.Completable;
import javax.inject.Inject;
import timber.log.Timber;

/** Top-level view model representing state of the {@link MainActivity} shared by all fragments. */
@SharedViewModel
public class MainViewModel extends AbstractViewModel {

  private final ProjectRepository projectRepository;
  private final FeatureRepository featureRepository;
  private final UserRepository userRepository;
  private final Navigator navigator;
  private EphemeralPopups popups;

  private MutableLiveData<WindowInsetsCompat> windowInsetsLiveData;

  @Inject
  public MainViewModel(
      ProjectRepository projectRepository,
      FeatureRepository featureRepository,
      UserRepository userRepository,
      Navigator navigator,
      AuthenticationManager authenticationManager,
      EphemeralPopups popups) {
    windowInsetsLiveData = new MutableLiveData<>();
    this.projectRepository = projectRepository;
    this.featureRepository = featureRepository;
    this.userRepository = userRepository;
    this.navigator = navigator;
    this.popups = popups;

    // TODO: Move to background service.
    disposeOnClear(
        projectRepository
            .getActiveProjectOnceAndStream()
            .switchMapCompletable(this::syncFeatures)
            .subscribe());

    disposeOnClear(authenticationManager.getSignInState().subscribe(this::onSignInStateChange));
  }

  /**
   * Keeps local features in sync with remote when a project is active, does nothing when no project
   * is active. The stream never completes; syncing stops when subscriptions are disposed of.
   *
   * @param projectLoadable the load state of the currently active project.
   */
  private Completable syncFeatures(Loadable<Project> projectLoadable) {
    return projectLoadable.value().map(featureRepository::syncFeatures).orElse(Completable.never());
  }

  public LiveData<WindowInsetsCompat> getWindowInsets() {
    return windowInsetsLiveData;
  }

  void onApplyWindowInsets(WindowInsetsCompat insets) {
    windowInsetsLiveData.setValue(insets);
  }

  private void onSignInStateChange(SignInState signInState) {
    Timber.d("Auth status change: %s", signInState.state());
    switch (signInState.state()) {
      case SIGNED_OUT:
        // TODO: Check auth status whenever fragments resumes.
        onSignedOut();
        break;
      case SIGNING_IN:
        // TODO: Show/hide spinner.
        break;
      case SIGNED_IN:
        signInState
            .getUser()
            .ifPresentOrElse(
                user -> userRepository.saveUser(user).subscribe(() -> onSignedIn()),
                () -> Timber.e("User signed in but missing"));
        break;
      case ERROR:
        onSignInError(signInState);
        break;
      default:
        Timber.e("Unhandled state: %s", signInState.state());
        break;
    }
  }

  private void onSignInError(SignInState signInState) {
    Timber.d("Authentication error : %s", signInState.error());
    popups.showError(R.string.sign_in_unsuccessful);
    onSignedOut();
  }

  void onSignedOut() {
    projectRepository.clearActiveProject();
    navigator.navigate(SignInFragmentDirections.showSignInScreen());
  }

  void onSignedIn() {
    navigator.navigate(HomeScreenFragmentDirections.showHomeScreen());
  }
}

@@ -788,24 +788,66 @@
 el.Project;%0A
+import com.google.android.gnd.model.User;%0A
 import com.g
@@ -2836,16 +2836,25 @@
 OnClear(
+%0A        
 authenti
@@ -2866,16 +2866,29 @@
 nManager
+%0A            
 .getSign
@@ -2896,25 +2896,49 @@
 nState()
-.subscrib
+%0A            .switchMapCompletabl
 e(this::
@@ -2957,16 +2957,41 @@
 eChange)
+%0A            .subscribe()
 );%0A  %7D%0A%0A
@@ -3658,36 +3658,43 @@
 %0A  %7D%0A%0A  private 
-void
+Completable
  onSignInStateCh
@@ -4053,234 +4053,158 @@
     
-signInState%0A            .getUser()%0A            .ifPresentOrElse(%0A                user -%3E userRepository.saveUser(user).subscribe(() -%3E onSignedIn()),%0A                () -%3E Timber.e(%22User signed in but missing%22));%0A        break
+User user = signInState.getUser().orElseThrow(IllegalStateException::new);%0A        return userRepository.saveUser(user).doOnComplete(this::onSignedIn)
 ;%0A  
@@ -4366,14 +4366,49 @@
 ak;%0A
-
     %7D%0A
+    return Completable.complete();%0A
   %7D%0A

Fix handling saveUser subscription