/*
 * Copyright (C) 2020 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.assist

import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.util.Log
import com.android.internal.app.AssistUtils
import com.android.internal.logging.InstanceId
import com.android.internal.logging.InstanceIdSequence
import com.android.internal.logging.UiEventLogger
import com.android.internal.util.FrameworkStatsLog
import com.android.systemui.assist.AssistantInvocationEvent.Companion.deviceStateFromLegacyDeviceState
import com.android.systemui.assist.AssistantInvocationEvent.Companion.eventFromLegacyInvocationType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.settings.UserTracker
import javax.inject.Inject

/** Class for reporting events related to Assistant sessions. */
@SysUISingleton
open class AssistLogger @Inject constructor(
    protected val context: Context,
    protected val uiEventLogger: UiEventLogger,
    private val assistUtils: AssistUtils,
    private val phoneStateMonitor: PhoneStateMonitor,
    private val userTracker: UserTracker,
) {

    private val instanceIdSequence = InstanceIdSequence(INSTANCE_ID_MAX)

    private var currentInstanceId: InstanceId? = null

    fun reportAssistantInvocationEventFromLegacy(
        legacyInvocationType: Int,
        isInvocationComplete: Boolean,
        assistantComponent: ComponentName? = null,
        legacyDeviceState: Int? = null
    ) {
        val deviceState = if (legacyDeviceState == null) {
            null
        } else {
            deviceStateFromLegacyDeviceState(legacyDeviceState)
        }
        reportAssistantInvocationEvent(
                eventFromLegacyInvocationType(legacyInvocationType, isInvocationComplete),
                assistantComponent,
                deviceState)
    }

    fun reportAssistantInvocationEvent(
        invocationEvent: UiEventLogger.UiEventEnum,
        assistantComponent: ComponentName? = null,
        deviceState: Int? = null
    ) {

        val assistComponentFinal = assistantComponent ?: getAssistantComponentForCurrentUser()

        val assistantUid = getAssistantUid(assistComponentFinal)

        val deviceStateFinal = deviceState
                ?: deviceStateFromLegacyDeviceState(phoneStateMonitor.phoneState)

        FrameworkStatsLog.write(
                FrameworkStatsLog.ASSISTANT_INVOCATION_REPORTED,
                invocationEvent.id,
                assistantUid,
                assistComponentFinal?.flattenToString() ?: "",
                getOrCreateInstanceId().id,
                deviceStateFinal,
                false)
        reportAssistantInvocationExtraData()
    }

    fun reportAssistantSessionEvent(sessionEvent: UiEventLogger.UiEventEnum) {
        val assistantComponent = getAssistantComponentForCurrentUser()
        val assistantUid = getAssistantUid(assistantComponent)
        uiEventLogger.logWithInstanceId(
                sessionEvent,
                assistantUid,
                assistantComponent?.flattenToString(),
                getOrCreateInstanceId())

        if (SESSION_END_EVENTS.contains(sessionEvent)) {
            clearInstanceId()
        }
    }

    protected open fun reportAssistantInvocationExtraData() {
    }

    protected fun getOrCreateInstanceId(): InstanceId {
        val instanceId = currentInstanceId ?: instanceIdSequence.newInstanceId()
        currentInstanceId = instanceId
        return instanceId
    }

    protected fun clearInstanceId() {
        currentInstanceId = null
    }

    protected fun getAssistantComponentForCurrentUser(): ComponentName? {
        return assistUtils.getAssistComponentForUser(userTracker.userId)
    }

    protected fun getAssistantUid(assistantComponent: ComponentName?): Int {
        if (assistantComponent == null) {
            return 0
        }

        var assistantUid = 0
        try {
            assistantUid = context.packageManager.getApplicationInfo(
                    assistantComponent.packageName, /* flags = */
                    0).uid
        } catch (e: PackageManager.NameNotFoundException) {
            Log.e(TAG, "Unable to find Assistant UID", e)
        }
        return assistantUid
    }

    companion object {
        protected const val TAG = "AssistLogger"

        private const val INSTANCE_ID_MAX = 1 shl 20

        private val SESSION_END_EVENTS =
                setOf(
                        AssistantSessionEvent.ASSISTANT_SESSION_INVOCATION_CANCELLED,
                        AssistantSessionEvent.ASSISTANT_SESSION_CLOSE)
    }
}