package com.android.systemui.qs

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.permission.PermissionGroupUsage
import android.permission.PermissionManager
import android.safetycenter.SafetyCenterManager
import android.view.View
import androidx.annotation.WorkerThread
import com.android.internal.R
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.appops.AppOpsController
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyChipEvent
import com.android.systemui.privacy.PrivacyDialogController
import com.android.systemui.privacy.PrivacyDialogControllerV2
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.statusbar.phone.StatusIconContainer
import java.util.concurrent.Executor
import javax.inject.Inject
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.shade.ShadeViewProviderModule.Companion.SHADE_HEADER
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import javax.inject.Named

interface ChipVisibilityListener {
    fun onChipVisibilityRefreshed(visible: Boolean)
}

/**
 * Controls privacy icons/chip residing in QS header which show up when app is using camera,
 * microphone or location.
 * Manages their visibility depending on privacy signals coming from [PrivacyItemController].
 *
 * Unlike typical controller extending [com.android.systemui.util.ViewController] this view doesn't
 * observe its attachment state because depending on where it is used, it might be never detached.
 * Instead, parent controller should use [onParentVisible] and [onParentInvisible] to "activate" or
 * "deactivate" this controller.
 */
class HeaderPrivacyIconsController @Inject constructor(
    private val privacyItemController: PrivacyItemController,
    private val uiEventLogger: UiEventLogger,
    @Named(SHADE_HEADER) private val privacyChip: OngoingPrivacyChip,
    private val privacyDialogController: PrivacyDialogController,
    private val privacyDialogControllerV2: PrivacyDialogControllerV2,
    private val privacyLogger: PrivacyLogger,
    @Named(SHADE_HEADER) private val iconContainer: StatusIconContainer,
    private val permissionManager: PermissionManager,
    @Background private val backgroundExecutor: Executor,
    @Main private val uiExecutor: Executor,
    private val activityStarter: ActivityStarter,
    private val appOpsController: AppOpsController,
    private val broadcastDispatcher: BroadcastDispatcher,
    private val safetyCenterManager: SafetyCenterManager,
    private val deviceProvisionedController: DeviceProvisionedController,
    private val featureFlags: FeatureFlags
) {

    var chipVisibilityListener: ChipVisibilityListener? = null
    private var listening = false
    private var micCameraIndicatorsEnabled = false
    private var locationIndicatorsEnabled = false
    private var privacyChipLogged = false
    private var safetyCenterEnabled = false
    private val cameraSlot = privacyChip.resources.getString(R.string.status_bar_camera)
    private val micSlot = privacyChip.resources.getString(R.string.status_bar_microphone)
    private val locationSlot = privacyChip.resources.getString(R.string.status_bar_location)

    private val safetyCenterReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            safetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled()
        }
    }

    val attachStateChangeListener = object : View.OnAttachStateChangeListener {
        override fun onViewAttachedToWindow(v: View) {
            broadcastDispatcher.registerReceiver(
                    safetyCenterReceiver,
                    IntentFilter(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED),
                    executor = backgroundExecutor
            )
        }

        override fun onViewDetachedFromWindow(v: View) {
            broadcastDispatcher.unregisterReceiver(safetyCenterReceiver)
        }
    }

    init {
        backgroundExecutor.execute {
            safetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled()
        }

        if (privacyChip.isAttachedToWindow()) {
            broadcastDispatcher.registerReceiver(
                    safetyCenterReceiver,
                    IntentFilter(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED),
                    executor = backgroundExecutor
            )
        }

        privacyChip.addOnAttachStateChangeListener(attachStateChangeListener)
    }

    private val picCallback: PrivacyItemController.Callback =
            object : PrivacyItemController.Callback {
        override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
            privacyChip.privacyList = privacyItems
            setChipVisibility(privacyItems.isNotEmpty())
        }

        override fun onFlagMicCameraChanged(flag: Boolean) {
            if (micCameraIndicatorsEnabled != flag) {
                micCameraIndicatorsEnabled = flag
                update()
            }
        }

        override fun onFlagLocationChanged(flag: Boolean) {
            if (locationIndicatorsEnabled != flag) {
                locationIndicatorsEnabled = flag
                update()
            }
        }

        private fun update() {
            updatePrivacyIconSlots()
            setChipVisibility(privacyChip.privacyList.isNotEmpty())
        }
    }

    private fun getChipEnabled() = micCameraIndicatorsEnabled || locationIndicatorsEnabled

    fun onParentVisible() {
        privacyChip.setOnClickListener {
            // Do not expand dialog while device is not provisioned
            if (!deviceProvisionedController.isDeviceProvisioned) return@setOnClickListener
            // If the privacy chip is visible, it means there were some indicators
            uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK)
            if (safetyCenterEnabled) {
                if (featureFlags.isEnabled(Flags.ENABLE_NEW_PRIVACY_DIALOG)) {
                    privacyDialogControllerV2.showDialog(privacyChip.context, privacyChip)
                } else {
                    showSafetyCenter()
                }
            } else {
                privacyDialogController.showDialog(privacyChip.context)
            }
        }
        setChipVisibility(privacyChip.visibility == View.VISIBLE)
        micCameraIndicatorsEnabled = privacyItemController.micCameraAvailable
        locationIndicatorsEnabled = privacyItemController.locationAvailable

        // Ignore privacy icons because they show in the space above QQS
        updatePrivacyIconSlots()
    }

    private fun showSafetyCenter() {
        backgroundExecutor.execute {
            val usage = ArrayList(permGroupUsage())
            privacyLogger.logUnfilteredPermGroupUsage(usage)
            val startSafetyCenter = Intent(Intent.ACTION_VIEW_SAFETY_CENTER_QS)
            startSafetyCenter.putParcelableArrayListExtra(PermissionManager.EXTRA_PERMISSION_USAGES,
                usage)
            startSafetyCenter.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            uiExecutor.execute {
                activityStarter.startActivity(startSafetyCenter, true,
                    ActivityLaunchAnimator.Controller.fromView(privacyChip))
            }
        }
    }

    @WorkerThread
    private fun permGroupUsage(): List<PermissionGroupUsage> {
        return permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted)
    }

    fun onParentInvisible() {
        chipVisibilityListener = null
        privacyChip.setOnClickListener(null)
    }

    fun startListening() {
        listening = true
        // Get the most up to date info
        micCameraIndicatorsEnabled = privacyItemController.micCameraAvailable
        locationIndicatorsEnabled = privacyItemController.locationAvailable
        privacyItemController.addCallback(picCallback)
    }

    fun stopListening() {
        listening = false
        privacyItemController.removeCallback(picCallback)
        privacyChipLogged = false
    }

    private fun setChipVisibility(visible: Boolean) {
        if (visible && getChipEnabled()) {
            privacyLogger.logChipVisible(true)
            // Makes sure that the chip is logged as viewed at most once each time QS is opened
            // mListening makes sure that the callback didn't return after the user closed QS
            if (!privacyChipLogged && listening) {
                privacyChipLogged = true
                uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW)
            }
        } else {
            privacyLogger.logChipVisible(false)
        }

        privacyChip.visibility = if (visible) View.VISIBLE else View.GONE
        chipVisibilityListener?.onChipVisibilityRefreshed(visible)
    }

    private fun updatePrivacyIconSlots() {
        if (getChipEnabled()) {
            if (micCameraIndicatorsEnabled) {
                iconContainer.addIgnoredSlot(cameraSlot)
                iconContainer.addIgnoredSlot(micSlot)
            } else {
                iconContainer.removeIgnoredSlot(cameraSlot)
                iconContainer.removeIgnoredSlot(micSlot)
            }
            if (locationIndicatorsEnabled) {
                iconContainer.addIgnoredSlot(locationSlot)
            } else {
                iconContainer.removeIgnoredSlot(locationSlot)
            }
        } else {
            iconContainer.removeIgnoredSlot(cameraSlot)
            iconContainer.removeIgnoredSlot(micSlot)
            iconContainer.removeIgnoredSlot(locationSlot)
        }
    }
}