/*
 * Copyright (C) 2024 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.settingslib.preference

import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import androidx.preference.PreferenceScreen
import com.android.settingslib.metadata.EXTRA_BINDING_SCREEN_ARGS
import com.android.settingslib.metadata.PreferenceScreenRegistry
import kotlinx.coroutines.CoroutineScope

/** Factory to create preference screen. */
class PreferenceScreenFactory {
    /** Preference manager to create/inflate preference screen. */
    val preferenceManager: PreferenceManager

    /**
     * Optional existing hierarchy to merge the new hierarchies into.
     *
     * Provide existing hierarchy will preserve the internal state (e.g. scrollbar position) for
     * [PreferenceFragmentCompat].
     */
    private val rootScreen: PreferenceScreen?

    /**
     * Factory constructor from preference fragment.
     *
     * The fragment must be within a valid lifecycle.
     */
    constructor(preferenceFragment: PreferenceFragmentCompat) {
        preferenceManager = preferenceFragment.preferenceManager
        rootScreen = preferenceFragment.preferenceScreen
    }

    /** Factory constructor from [Context]. */
    @SuppressLint("RestrictedApi") constructor(context: Context) : this(PreferenceManager(context))

    /** Factory constructor from [PreferenceManager]. */
    constructor(preferenceManager: PreferenceManager) {
        this.preferenceManager = preferenceManager
        rootScreen = null
    }

    /** Context of the factory to create preference screen. */
    val context: Context
        get() = preferenceManager.context

    /** Returns the existing hierarchy or create a new empty preference screen. */
    fun getOrCreatePreferenceScreen(): PreferenceScreen =
        rootScreen ?: preferenceManager.createPreferenceScreen(context)

    /**
     * Inflates [PreferenceScreen] from xml resource.
     *
     * @param xmlRes The resource ID of the XML to inflate
     * @return The root hierarchy (if one was not provided, the new hierarchy's root)
     */
    @SuppressLint("RestrictedApi")
    fun inflate(xmlRes: Int): PreferenceScreen? =
        if (xmlRes != 0) {
            preferenceManager.inflateFromResource(preferenceManager.context, xmlRes, rootScreen)
        } else {
            rootScreen
        }

    /**
     * Creates [PreferenceScreen] of given key.
     *
     * The screen must be registered in [PreferenceScreenFactory] and provide a complete hierarchy.
     */
    fun createBindingScreen(
        context: Context,
        screenKey: String?,
        args: Bundle?,
        coroutineScope: CoroutineScope,
    ): PreferenceScreen? {
        val metadata = PreferenceScreenRegistry.create(context, screenKey, args) ?: return null
        if (metadata is PreferenceScreenCreator && metadata.hasCompleteHierarchy()) {
            return metadata.createPreferenceScreen(this, coroutineScope)
        }
        return null
    }

    companion object {
        /** Creates [PreferenceScreen] from [PreferenceScreenRegistry]. */
        @JvmStatic
        fun createBindingScreen(
            preference: Preference,
            coroutineScope: CoroutineScope,
        ): PreferenceScreen? {
            val context = preference.context
            val args = preference.peekExtras()?.getBundle(EXTRA_BINDING_SCREEN_ARGS)
            val preferenceScreenCreator =
                (PreferenceScreenRegistry.create(context, preference.key, args)
                    as? PreferenceScreenCreator) ?: return null
            if (!preferenceScreenCreator.hasCompleteHierarchy()) return null
            val factory = PreferenceScreenFactory(context)
            val preferenceScreen =
                preferenceScreenCreator.createPreferenceScreen(factory, coroutineScope)
            factory.preferenceManager.setPreferences(preferenceScreen)
            return preferenceScreen
        }
    }
}
