/*
 * 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.systemui.statusbar.notification.interruption

import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.CoreStartable
import com.android.systemui.statusbar.notification.collection.NotificationEntry

/**
 * Decides whether a notification should visually interrupt the user in various ways.
 *
 * These include displaying the notification as heads-up (peeking while the device is awake or
 * pulsing while the device is dozing), displaying the notification as a bubble, and launching a
 * full-screen intent for the notification.
 */
interface VisualInterruptionDecisionProvider : CoreStartable {
    /**
     * Represents the decision to visually interrupt or not.
     *
     * Used for heads-up and bubble decisions; subclassed by [FullScreenIntentDecision] for
     * full-screen intent decisions.
     *
     * @property[shouldInterrupt] whether a visual interruption should be triggered
     * @property[logReason] a log-friendly string explaining the reason for the decision; should be
     *   used *only* for logging, not decision-making
     */
    interface Decision {
        val shouldInterrupt: Boolean
        val logReason: String
    }

    /**
     * Represents the decision to launch a full-screen intent for a notification or not.
     *
     * @property[wouldInterruptWithoutDnd] whether a full-screen intent should not be launched only
     *   because Do Not Disturb has suppressed it
     */
    interface FullScreenIntentDecision : Decision {
        val wouldInterruptWithoutDnd: Boolean
    }

    /** Initializes the provider. */
    override fun start() {}

    /**
     * Adds a [NotificationInterruptSuppressor] that can suppress visual interruptions.
     *
     * This method may be called before [start] has been called.
     *
     * This class may call suppressors, conditions, and filters in any order.
     *
     * @param[suppressor] the suppressor to add
     */
    fun addLegacySuppressor(suppressor: NotificationInterruptSuppressor)

    /**
     * Removes a previously-added suppressor.
     *
     * This method may be called before [start] has been called.
     *
     * @param[suppressor] the suppressor to remove
     */
    @VisibleForTesting fun removeLegacySuppressor(suppressor: NotificationInterruptSuppressor)

    /**
     * Adds a [VisualInterruptionCondition] that can suppress visual interruptions without examining
     * individual notifications.
     *
     * This method may be called before [start] has been called.
     *
     * This class may call suppressors, conditions, and filters in any order.
     *
     * @param[condition] the condition to add
     */
    fun addCondition(condition: VisualInterruptionCondition)

    /**
     * Removes a previously-added condition.
     *
     * This method may be called before [start] has been called.
     *
     * @param[condition] the condition to remove
     */
    @VisibleForTesting fun removeCondition(condition: VisualInterruptionCondition)

    /**
     * Adds a [VisualInterruptionFilter] that can suppress visual interruptions based on individual
     * notifications.
     *
     * This method may be called before [start] has been called.
     *
     * This class may call suppressors, conditions, and filters in any order.
     *
     * @param[filter] the filter to add
     */
    fun addFilter(filter: VisualInterruptionFilter)

    /**
     * Removes a previously-added filter.
     *
     * This method may be called before [start] has been called.
     *
     * @param[filter] the filter to remove
     */
    @VisibleForTesting fun removeFilter(filter: VisualInterruptionFilter)

    /**
     * Decides whether a [notification][entry] should display as heads-up or not, but does not log
     * that decision.
     *
     * [start] must be called before this method can be called.
     *
     * @param[entry] the notification that this decision is about
     * @return the decision to display that notification as heads-up or not
     */
    fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision

    /**
     * Decides whether a [notification][entry] should display as heads-up or not, and logs that
     * decision.
     *
     * If the device is awake, the decision will consider whether the notification should "peek"
     * (slide in from the top of the screen over the current activity).
     *
     * If the device is dozing, the decision will consider whether the notification should "pulse"
     * (wake the screen up and display the ambient view of the notification).
     *
     * [start] must be called before this method can be called.
     *
     * @see[makeUnloggedHeadsUpDecision]
     *
     * @param[entry] the notification that this decision is about
     * @return the decision to display that notification as heads-up or not
     */
    fun makeAndLogHeadsUpDecision(entry: NotificationEntry): Decision

    /**
     * Decides whether a [notification][entry] should launch a full-screen intent or not, but does
     * not log that decision.
     *
     * The returned decision can be logged by passing it to [logFullScreenIntentDecision].
     *
     * [start] must be called before this method can be called.
     *
     * @see[makeAndLogHeadsUpDecision]
     *
     * @param[entry] the notification that this decision is about
     * @return the decision to launch a full-screen intent for that notification or not
     */
    fun makeUnloggedFullScreenIntentDecision(entry: NotificationEntry): FullScreenIntentDecision

    /**
     * Logs a previous [decision] to launch a full-screen intent or not.
     *
     * [start] must be called before this method can be called.
     *
     * @param[decision] the decision to log
     */
    fun logFullScreenIntentDecision(decision: FullScreenIntentDecision)

    /**
     * Decides whether a [notification][entry] should display as a bubble or not.
     *
     * [start] must be called before this method can be called.
     *
     * @param[entry] the notification that this decision is about
     * @return the decision to display that notification as a bubble or not
     */
    fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision
}
