/*
 * Copyright (C) 2022 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.systemui.statusbar.policy

import android.content.Context
import android.content.Intent
import android.view.View
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
import com.android.systemui.user.data.source.UserRecord
import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import dagger.Lazy
import java.io.PrintWriter
import java.lang.ref.WeakReference
import javax.inject.Inject

/** Access point into multi-user switching logic. */
@Deprecated("Use UserInteractor or GuestUserInteractor instead.")
@SysUISingleton
class UserSwitcherController
@Inject
constructor(
    @Application private val applicationContext: Context,
    private val userSwitcherInteractorLazy: Lazy<UserSwitcherInteractor>,
    private val guestUserInteractorLazy: Lazy<GuestUserInteractor>,
    private val keyguardInteractorLazy: Lazy<KeyguardInteractor>,
    private val activityStarter: ActivityStarter,
) {

    /** Defines interface for classes that can be called back when the user is switched. */
    fun interface UserSwitchCallback {
        /** Notifies that the user has switched. */
        fun onUserSwitched()
    }

    private val mUserSwitcherInteractor: UserSwitcherInteractor by lazy {
        userSwitcherInteractorLazy.get()
    }
    private val guestUserInteractor: GuestUserInteractor by lazy { guestUserInteractorLazy.get() }
    private val keyguardInteractor: KeyguardInteractor by lazy { keyguardInteractorLazy.get() }

    private val callbackCompatMap =
        mutableMapOf<UserSwitchCallback, UserSwitcherInteractor.UserCallback>()

    /** The current list of [UserRecord]. */
    val users: ArrayList<UserRecord>
        get() = mUserSwitcherInteractor.userRecords.value

    /** Whether the user switcher experience should use the simple experience. */
    val isSimpleUserSwitcher: Boolean
        get() = mUserSwitcherInteractor.isSimpleUserSwitcher

    val isUserSwitcherEnabled: Boolean
        get() = mUserSwitcherInteractor.isUserSwitcherEnabled

    /** The [UserRecord] of the current user or `null` when none. */
    val currentUserRecord: UserRecord?
        get() = mUserSwitcherInteractor.selectedUserRecord.value

    /** The name of the current user of the device or `null`, when none is selected. */
    val currentUserName: String?
        get() =
            currentUserRecord?.let {
                LegacyUserUiHelper.getUserRecordName(
                    context = applicationContext,
                    record = it,
                    isGuestUserAutoCreated = mUserSwitcherInteractor.isGuestUserAutoCreated,
                    isGuestUserResetting = mUserSwitcherInteractor.isGuestUserResetting,
                )
            }

    /**
     * Notifies that a user has been selected.
     *
     * This will trigger the right user journeys to create a guest user, switch users, and/or
     * navigate to the correct destination.
     *
     * If a user with the given ID is not found, this method is a no-op.
     *
     * @param userId The ID of the user to switch to.
     * @param dialogShower An optional [DialogShower] in case we need to show dialogs.
     */
    fun onUserSelected(userId: Int, dialogShower: DialogShower?) {
        mUserSwitcherInteractor.selectUser(userId, dialogShower)
    }

    /** Whether the guest user is configured to always be present on the device. */
    val isGuestUserAutoCreated: Boolean
        get() = mUserSwitcherInteractor.isGuestUserAutoCreated

    /** Whether the guest user is currently being reset. */
    val isGuestUserResetting: Boolean
        get() = mUserSwitcherInteractor.isGuestUserResetting

    /** Registers an adapter to notify when the users change. */
    fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>) {
        mUserSwitcherInteractor.addCallback(
            object : UserSwitcherInteractor.UserCallback {
                override fun isEvictable(): Boolean {
                    return adapter.get() == null
                }

                override fun onUserStateChanged() {
                    adapter.get()?.notifyDataSetChanged()
                }
            }
        )
    }

    /** Notifies the item for a user has been clicked. */
    fun onUserListItemClicked(
        record: UserRecord,
        dialogShower: DialogShower?,
    ) {
        mUserSwitcherInteractor.onRecordSelected(record, dialogShower)
    }

    /**
     * Removes guest user and switches to target user. The guest must be the current user and its id
     * must be `guestUserId`.
     *
     * If `targetUserId` is `UserHandle.USER_NULL`, then create a new guest user in the foreground,
     * and immediately switch to it. This is used for wiping the current guest and replacing it with
     * a new one.
     *
     * If `targetUserId` is specified, then remove the guest in the background while switching to
     * `targetUserId`.
     *
     * If device is configured with `config_guestUserAutoCreated`, then after guest user is removed,
     * a new one is created in the background. This has no effect if `targetUserId` is
     * `UserHandle.USER_NULL`.
     *
     * @param guestUserId id of the guest user to remove
     * @param targetUserId id of the user to switch to after guest is removed. If
     *   `UserHandle.USER_NULL`, then switch immediately to the newly created guest user.
     */
    fun removeGuestUser(guestUserId: Int, targetUserId: Int) {
        mUserSwitcherInteractor.removeGuestUser(
            guestUserId = guestUserId,
            targetUserId = targetUserId,
        )
    }

    /**
     * Exits guest user and switches to previous non-guest user. The guest must be the current user.
     *
     * @param guestUserId user id of the guest user to exit
     * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when
     *   target user id is not known
     * @param forceRemoveGuestOnExit true: remove guest before switching user, false: remove guest
     *   only if its ephemeral, else keep guest
     */
    fun exitGuestUser(guestUserId: Int, targetUserId: Int, forceRemoveGuestOnExit: Boolean) {
        mUserSwitcherInteractor.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit)
    }

    /**
     * Guarantee guest is present only if the device is provisioned. Otherwise, create a content
     * observer to wait until the device is provisioned, then schedule the guest creation.
     */
    fun schedulePostBootGuestCreation() {
        guestUserInteractor.onDeviceBootCompleted()
    }

    /** Whether keyguard is showing. */
    val isKeyguardShowing: Boolean
        get() = keyguardInteractor.isKeyguardShowing()

    /** Starts an activity with the given [Intent]. */
    fun startActivity(intent: Intent) {
        activityStarter.startActivity(intent, /* dismissShade= */ true)
    }

    /**
     * Refreshes users from UserManager.
     *
     * The pictures are only loaded if they have not been loaded yet.
     */
    fun refreshUsers() {
        mUserSwitcherInteractor.refreshUsers()
    }

    /** Adds a subscriber to when user switches. */
    fun addUserSwitchCallback(callback: UserSwitchCallback) {
        val interactorCallback =
            object : UserSwitcherInteractor.UserCallback {
                override fun onUserStateChanged() {
                    callback.onUserSwitched()
                }
            }
        callbackCompatMap[callback] = interactorCallback
        mUserSwitcherInteractor.addCallback(interactorCallback)
    }

    /** Removes a previously-added subscriber. */
    fun removeUserSwitchCallback(callback: UserSwitchCallback) {
        val interactorCallback = callbackCompatMap.remove(callback)
        if (interactorCallback != null) {
            mUserSwitcherInteractor.removeCallback(interactorCallback)
        }
    }

    fun dump(pw: PrintWriter, args: Array<out String>) {
        mUserSwitcherInteractor.dump(pw)
    }

    companion object {
        /** Alpha value to apply to a user view in the user switcher when it's selectable. */
        private const val ENABLED_ALPHA =
            LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA

        /** Alpha value to apply to a user view in the user switcher when it's not selectable. */
        private const val DISABLED_ALPHA =
            LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA

        @JvmStatic
        fun setSelectableAlpha(view: View) {
            view.alpha =
                if (view.isEnabled) {
                    ENABLED_ALPHA
                } else {
                    DISABLED_ALPHA
                }
        }
    }
}
