/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * 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
 *
 *      http://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.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel

import com.android.settings.biometrics.fingerprint2.lib.model.FastEnroll
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintFlow
import com.android.systemui.biometrics.shared.model.FingerprintSensor

/**
 * A [FingerprintAction] event notifies the current [FingerprintNavigationStep] that an event
 * occurred. Depending on the type of [FingerprintAction] and the current
 * [FingerprintNavigationStep], the navstep will potentially produce a new
 * [FingerprintNavigationStep] indicating either 1). Control flow has changed 2). The activity has
 * finished 3). A transition is required
 */
enum class FingerprintAction {
  NEXT,
  PREV,
  CONFIRM_DEVICE_SUCCESS,
  CONFIRM_DEVICE_FAIL,
  TRANSITION_FINISHED,
  DID_GO_TO_BACKGROUND,
  ACTIVITY_CREATED,
  NEGATIVE_BUTTON_PRESSED,
  USER_CLICKED_FINISH,
  ADD_ANOTHER,
}

/** State that can be used to help a [FingerprintNavigationStep] determine the next step to take. */
data class NavigationState(
  val flowType: FingerprintFlow,
  val hasConfirmedDeviceCredential: Boolean,
  val fingerprintSensor: FingerprintSensor?,
)

/**
 * A generic interface for operating on (state, action) -> state? which will produce either another
 * FingerprintNavStep if something is required, or nothing.
 *
 * Note during the lifetime of the Activity, their should only be one [FingerprintNavigationStep] at
 * a time.
 */
sealed interface FingerprintNavigationStep {
  fun update(state: NavigationState, action: FingerprintAction): FingerprintNavigationStep?

  /**
   * This indicates that a transition should occur from one screen to another. This class should
   * contain all necessary info about the transition.
   *
   * A transition step will cause a screen to change ownership from the current screen to the
   * [nextUiStep], after the transition has been completed and a
   * [FingerprintAction.TRANSITION_FINISHED] has been sent, the [nextUiStep] will be given control.
   */
  class TransitionStep(val nextUiStep: UiStep) : FingerprintNavigationStep {
    override fun update(
      state: NavigationState,
      action: FingerprintAction,
    ): FingerprintNavigationStep? {
      return when (action) {
        FingerprintAction.TRANSITION_FINISHED -> nextUiStep
        else -> null
      }
    }

    override fun toString(): String = "TransitionStep(nextUiStep=$nextUiStep)"
  }

  /** Indicates we should finish the enrolling activity */
  data class Finish(val result: Int?) : FingerprintNavigationStep {
    override fun update(
      state: NavigationState,
      action: FingerprintAction,
    ): FingerprintNavigationStep? = null
  }

  /** UiSteps should have a 1 to 1 mapping between each screen of FingerprintEnrollment */
  sealed class UiStep(
    val enterTransition: Transition = Transition.EnterFromRight,
    val exitTransition: Transition = Transition.ExitToLeft,
  ) : FingerprintNavigationStep

  /** This is the landing page for enrollment, where no content is shown. */
  data object Init : UiStep() {
    override fun update(
      state: NavigationState,
      action: FingerprintAction,
    ): FingerprintNavigationStep? {
      return when (action) {
        FingerprintAction.ACTIVITY_CREATED -> {
          if (!state.hasConfirmedDeviceCredential) {
            TransitionStep(ConfirmDeviceCredential)
          } else if (state.flowType is FastEnroll) {
            TransitionStep(Enrollment(state.fingerprintSensor!!))
          } else {
            TransitionStep(Introduction())
          }
        }
        else -> null
      }
    }
  }

  /** Indicates the ConfirmDeviceCredential activity is being presented to the user */
  data object ConfirmDeviceCredential : UiStep() {
    override fun update(
      state: NavigationState,
      action: FingerprintAction,
    ): FingerprintNavigationStep? {
      return when (action) {
        FingerprintAction.CONFIRM_DEVICE_SUCCESS -> TransitionStep(Introduction())
        FingerprintAction.CONFIRM_DEVICE_FAIL -> Finish(null)
        else -> null
      }
    }
  }

  /** Indicates the FingerprintIntroduction screen is being presented to the user */
  class Introduction(
    enterTransition: Transition = Transition.EnterFromRight,
    exitTransition: Transition = Transition.ExitToLeft,
  ) : UiStep(enterTransition, exitTransition) {
    override fun update(
      state: NavigationState,
      action: FingerprintAction,
    ): FingerprintNavigationStep? {
      return when (action) {
        FingerprintAction.NEXT -> TransitionStep(Education(state.fingerprintSensor!!))
        FingerprintAction.NEGATIVE_BUTTON_PRESSED,
        FingerprintAction.PREV -> Finish(null)
        else -> null
      }
    }
  }

  /** Indicates the FingerprintEducation screen is being presented to the user */
  class Education(
    val sensor: FingerprintSensor,
    enterTransition: Transition = Transition.EnterFromRight,
    exitTransition: Transition = Transition.ExitToLeft,
  ) : UiStep(enterTransition, exitTransition) {
    override fun update(
      state: NavigationState,
      action: FingerprintAction,
    ): FingerprintNavigationStep? {
      return when (action) {
        FingerprintAction.NEXT -> TransitionStep(Enrollment(state.fingerprintSensor!!))
        FingerprintAction.NEGATIVE_BUTTON_PRESSED,
        FingerprintAction.PREV ->
          TransitionStep(Introduction(Transition.EnterFromLeft, Transition.ExitToRight))
        else -> null
      }
    }
  }

  /** Indicates the Enrollment screen is being presented to the user */
  data class Enrollment(val sensor: FingerprintSensor) : UiStep() {
    override fun update(
      state: NavigationState,
      action: FingerprintAction,
    ): FingerprintNavigationStep? {
      return when (action) {
        FingerprintAction.NEXT -> TransitionStep(Confirmation)
        FingerprintAction.NEGATIVE_BUTTON_PRESSED,
        FingerprintAction.USER_CLICKED_FINISH,
        FingerprintAction.DID_GO_TO_BACKGROUND -> Finish(null)
        else -> null
      }
    }
  }

  /** Indicates the Confirmation screen is being presented to the user */
  data object Confirmation : UiStep() {
    override fun update(
      state: NavigationState,
      action: FingerprintAction,
    ): FingerprintNavigationStep? {
      return when (action) {
        FingerprintAction.NEXT -> Finish(null)
        FingerprintAction.PREV ->
          TransitionStep(
            Education(state.fingerprintSensor!!, Transition.EnterFromLeft, Transition.ExitToRight)
          )
        FingerprintAction.ADD_ANOTHER -> TransitionStep(Enrollment(state.fingerprintSensor!!))
        else -> null
      }
    }
  }
}
