/*
 * 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.controls.controller

import android.app.job.JobInfo
import android.app.job.JobParameters
import android.app.job.JobService
import android.content.ComponentName
import android.content.Context
import android.os.PersistableBundle
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.backup.BackupHelper
import com.android.systemui.settings.UserFileManagerImpl
import java.io.File
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit

/**
 * Class to track the auxiliary persistence of controls.
 *
 * This file is a copy of the `controls_favorites.xml` file restored from a back up. It is used to
 * keep track of controls that were restored but its corresponding app has not been installed yet.
 */
class AuxiliaryPersistenceWrapper
@VisibleForTesting
internal constructor(wrapper: ControlsFavoritePersistenceWrapper) {

    constructor(
        file: File,
        executor: Executor
    ) : this(ControlsFavoritePersistenceWrapper(file, executor))

    companion object {
        const val AUXILIARY_FILE_NAME = "aux_controls_favorites.xml"
    }

    private var persistenceWrapper: ControlsFavoritePersistenceWrapper = wrapper

    /** Access the current list of favorites as tracked by the auxiliary file */
    var favorites: List<StructureInfo> = emptyList()
        private set

    init {
        initialize()
    }

    /**
     * Change the file that this class is tracking.
     *
     * This will reset [favorites].
     */
    fun changeFile(file: File) {
        persistenceWrapper.changeFileAndBackupManager(file, null)
        initialize()
    }

    /**
     * Initialize the list of favorites to the content of the auxiliary file. If the file does not
     * exist, it will be initialized to an empty list.
     */
    fun initialize() {
        favorites =
            if (persistenceWrapper.fileExists) {
                persistenceWrapper.readFavorites()
            } else {
                emptyList()
            }
    }

    /**
     * Gets the list of favorite controls as persisted in the auxiliary file for a given component.
     *
     * When the favorites for that application are returned, they will be removed from the auxiliary
     * file immediately, so they won't be retrieved again.
     *
     * @param componentName the name of the service that provided the controls
     * @return a list of structures with favorites
     */
    fun getCachedFavoritesAndRemoveFor(componentName: ComponentName): List<StructureInfo> {
        if (!persistenceWrapper.fileExists) {
            return emptyList()
        }
        val (comp, noComp) = favorites.partition { it.componentName == componentName }
        return comp.also {
            favorites = noComp
            if (favorites.isNotEmpty()) {
                persistenceWrapper.storeFavorites(noComp)
            } else {
                persistenceWrapper.deleteFile()
            }
        }
    }

    /** [JobService] to delete the auxiliary file after a week. */
    class DeletionJobService : JobService() {
        companion object {
            @VisibleForTesting internal val DELETE_FILE_JOB_ID = 1000
            @VisibleForTesting internal val USER = "USER"
            private val WEEK_IN_MILLIS = TimeUnit.DAYS.toMillis(7)
            fun getJobForContext(context: Context, targetUserId: Int): JobInfo {
                val jobId = DELETE_FILE_JOB_ID + context.userId
                val componentName = ComponentName(context, DeletionJobService::class.java)
                val bundle = PersistableBundle().also { it.putInt(USER, targetUserId) }
                return JobInfo.Builder(jobId, componentName)
                    .setMinimumLatency(WEEK_IN_MILLIS)
                    .setPersisted(true)
                    .setExtras(bundle)
                    .build()
            }
        }

        @VisibleForTesting
        fun attachContext(context: Context) {
            attachBaseContext(context)
        }

        override fun onStartJob(params: JobParameters): Boolean {
            val userId = params.getExtras()?.getInt(USER, 0) ?: 0
            synchronized(BackupHelper.controlsDataLock) {
                val file =
                    UserFileManagerImpl.createFile(
                        userId = userId,
                        fileName = AUXILIARY_FILE_NAME,
                    )
                baseContext.deleteFile(file.getPath())
            }
            return false
        }

        override fun onStopJob(params: JobParameters?): Boolean {
            return true // reschedule and try again if the job was stopped without completing
        }
    }
}
