/*
 * Copyright (C) 2015 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.settings.applications.intentpicker;

import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_NONE;
import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_SELECTED;
import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_VERIFIED;

import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationUserState;
import android.icu.text.MessageFormat;
import android.net.Uri;
import android.os.Bundle;
import android.util.ArraySet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Switch;
import android.widget.TextView;

import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;

import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.ClearDefaultsPreference;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.MainSwitchPreference;
import com.android.settingslib.widget.OnMainSwitchChangeListener;

import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/** The page of the Open by default */
public class AppLaunchSettings extends AppInfoBase implements
        Preference.OnPreferenceChangeListener, OnMainSwitchChangeListener {
    private static final String TAG = "AppLaunchSettings";
    // Preference keys
    private static final String MAIN_SWITCH_PREF_KEY = "open_by_default_supported_links";
    private static final String VERIFIED_LINKS_PREF_KEY = "open_by_default_verified_links";
    private static final String ADD_LINK_PREF_KEY = "open_by_default_add_link";
    private static final String CLEAR_DEFAULTS_PREF_KEY = "app_launch_clear_defaults";
    private static final String FOOTER_PREF_KEY = "open_by_default_footer";

    private static final String MAIN_PREF_CATEGORY_KEY = "open_by_default_main_category";
    private static final String SELECTED_LINKS_CATEGORY_KEY =
            "open_by_default_selected_links_category";
    private static final String OTHER_DETAILS_PREF_CATEGORY_KEY = "app_launch_other_defaults";

    private static final String LEARN_MORE_URI =
            "https://developer.android.com/training/app-links/verify-site-associations";

    // Dialogs id
    private static final int DLG_VERIFIED_LINKS = DLG_BASE + 1;

    // Arguments key
    public static final String APP_PACKAGE_KEY = "app_package";

    private ClearDefaultsPreference mClearDefaultsPreference;
    private MainSwitchPreference mMainSwitchPreference;
    private Preference mAddLinkPreference;
    private PreferenceCategory mMainPreferenceCategory;
    private PreferenceCategory mSelectedLinksPreferenceCategory;
    private PreferenceCategory mOtherDefaultsPreferenceCategory;

    private boolean mActivityCreated;

    @VisibleForTesting
    Context mContext;
    @VisibleForTesting
    DomainVerificationManager mDomainVerificationManager;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mContext = context;
        mActivityCreated = false;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (mAppEntry == null) {
            Log.w(TAG, "onCreate: mAppEntry is null, please check the reason!!!");
            getActivity().finish();
            return;
        }
        addPreferencesFromResource(R.xml.installed_app_launch_settings);
        mDomainVerificationManager = mContext.getSystemService(DomainVerificationManager.class);
        initUIComponents();
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        createHeaderPreference();
    }

    @Override
    public int getMetricsCategory() {
        return SettingsEnums.APPLICATIONS_APP_LAUNCH;
    }

    @Override
    protected AlertDialog createDialog(int id, int errorCode) {
        if (id == DLG_VERIFIED_LINKS) {
            return createVerifiedLinksDialog();
        }
        return null;
    }

    @Override
    protected boolean refreshUi() {
        mClearDefaultsPreference.setPackageName(mPackageName);
        mClearDefaultsPreference.setAppEntry(mAppEntry);
        return true;
    }

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        final boolean isChecked = (boolean) newValue;
        IntentPickerUtils.logd(
                "onPreferenceChange: " + preference.getTitle() + " isChecked: " + isChecked);
        if ((preference instanceof LeftSideCheckBoxPreference) && !isChecked) {
            final Set<String> domainSet = new ArraySet<>();
            domainSet.add(preference.getTitle().toString());
            removePreference(preference.getKey());
            final DomainVerificationUserState userState =
                    IntentPickerUtils.getDomainVerificationUserState(mDomainVerificationManager,
                            mPackageName);
            if (userState == null) {
                return false;
            }
            setDomainVerificationUserSelection(userState.getIdentifier(), domainSet, /* enabled= */
                    false);
            mAddLinkPreference.setEnabled(isAddLinksNotEmpty());
        }
        return true;
    }

    @Override
    public void onSwitchChanged(Switch switchView, boolean isChecked) {
        IntentPickerUtils.logd("onSwitchChanged: isChecked=" + isChecked);
        if (mMainSwitchPreference != null) { //mMainSwitchPreference synced with Switch
            mMainSwitchPreference.setChecked(isChecked);
        }
        if (mMainPreferenceCategory != null) {
            mMainPreferenceCategory.setVisible(isChecked);
        }
        if (mDomainVerificationManager != null) {
            try {
                mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(mPackageName,
                        isChecked);
            } catch (PackageManager.NameNotFoundException e) {
                Log.w(TAG, "onSwitchChanged: " + e.getMessage());
            }
        }
    }

    private void createHeaderPreference() {
        if (mActivityCreated) {
            Log.w(TAG, "onParentActivityCreated: ignoring duplicate call.");
            return;
        }
        mActivityCreated = true;
        if (mPackageInfo == null) {
            Log.w(TAG, "onParentActivityCreated: PakcageInfo is null.");
            return;
        }
        final Activity activity = getActivity();
        final String summary = activity.getString(R.string.app_launch_top_intro_message);
        final Preference pref = EntityHeaderController
                .newInstance(activity, this, null /* header */)
                .setRecyclerView(getListView(), getSettingsLifecycle())
                .setIcon(Utils.getBadgedIcon(mContext, mPackageInfo.applicationInfo))
                .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm))
                .setSummary(summary)  // add intro text
                .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
                .setPackageName(mPackageName)
                .setUid(mPackageInfo.applicationInfo.uid)
                .setHasAppInfoLink(true)
                .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
                        EntityHeaderController.ActionType.ACTION_NONE)
                .done(activity, getPrefContext());
        getPreferenceScreen().addPreference(pref);
    }

    private void initUIComponents() {
        initMainSwitchAndCategories();
        if (canUpdateMainSwitchAndCategories()) {
            initVerifiedLinksPreference();
            initAddLinkPreference();
            addSelectedLinksPreference();
            initFooter();
        }
    }

    private void initMainSwitchAndCategories() {
        mMainSwitchPreference = (MainSwitchPreference) findPreference(MAIN_SWITCH_PREF_KEY);
        mMainPreferenceCategory = findPreference(MAIN_PREF_CATEGORY_KEY);
        mSelectedLinksPreferenceCategory = findPreference(SELECTED_LINKS_CATEGORY_KEY);
        // Initialize the "Other Default Category" section
        initOtherDefaultsSection();
    }

    private boolean canUpdateMainSwitchAndCategories() {
        final DomainVerificationUserState userState =
                IntentPickerUtils.getDomainVerificationUserState(mDomainVerificationManager,
                        mPackageName);
        if (userState == null) {
            disabledPreference();
            return false;
        }

        IntentPickerUtils.logd("isLinkHandlingAllowed() : " + userState.isLinkHandlingAllowed());
        mMainSwitchPreference.updateStatus(userState.isLinkHandlingAllowed());
        mMainSwitchPreference.addOnSwitchChangeListener(this);
        mMainPreferenceCategory.setVisible(userState.isLinkHandlingAllowed());
        return true;
    }

    /** Initialize verified links preference */
    private void initVerifiedLinksPreference() {
        final Preference verifiedLinksPreference = mMainPreferenceCategory.findPreference(
                VERIFIED_LINKS_PREF_KEY);
        verifiedLinksPreference.setOnPreferenceClickListener(preference -> {
            showVerifiedLinksDialog();
            return true;
        });
        final int verifiedLinksNo = getLinksNumber(DOMAIN_STATE_VERIFIED);
        verifiedLinksPreference.setTitle(getVerifiedLinksTitle(verifiedLinksNo));
        verifiedLinksPreference.setEnabled(verifiedLinksNo > 0);
    }

    private void showVerifiedLinksDialog() {
        final int linksNo = getLinksNumber(DOMAIN_STATE_VERIFIED);
        if (linksNo == 0) {
            return;
        }
        showDialogInner(DLG_VERIFIED_LINKS, /* moveErrorCode= */ 0);
    }

    private AlertDialog createVerifiedLinksDialog() {
        final int linksNo = getLinksNumber(DOMAIN_STATE_VERIFIED);

        final View titleView = LayoutInflater.from(mContext).inflate(
                R.layout.app_launch_verified_links_title, /* root= */ null);
        ((TextView) titleView.findViewById(R.id.dialog_title)).setText(
                getVerifiedLinksTitle(linksNo));
        ((TextView) titleView.findViewById(R.id.dialog_message)).setText(
                getVerifiedLinksMessage(linksNo));

        final List<String> verifiedLinksList = IntentPickerUtils.getLinksList(
                mDomainVerificationManager, mPackageName, DOMAIN_STATE_VERIFIED);
        AlertDialog dialog = new AlertDialog.Builder(mContext)
                .setCustomTitle(titleView)
                .setCancelable(true)
                .setItems(verifiedLinksList.toArray(new String[0]), /* listener= */ null)
                .setPositiveButton(R.string.app_launch_dialog_ok, /* listener= */ null)
                .create();
        if (dialog.getListView() != null) {
            dialog.getListView().setTextDirection(View.TEXT_DIRECTION_LOCALE);
        } else {
            Log.w(TAG, "createVerifiedLinksDialog: dialog.getListView() is null, please check it.");
        }
        return dialog;
    }

    @VisibleForTesting
    String getVerifiedLinksTitle(int linksNo) {
        MessageFormat msgFormat = new MessageFormat(
                getResources().getString(R.string.app_launch_verified_links_title),
                Locale.getDefault());
        Map<String, Object> arguments = new HashMap<>();
        arguments.put("count", linksNo);
        return msgFormat.format(arguments);
    }

    private String getVerifiedLinksMessage(int linksNo) {
        MessageFormat msgFormat = new MessageFormat(
                getResources().getString(R.string.app_launch_verified_links_message),
                Locale.getDefault());
        Map<String, Object> arguments = new HashMap<>();
        arguments.put("count", linksNo);
        return msgFormat.format(arguments);
    }

    /** Add selected links items */
    void addSelectedLinksPreference() {
        if (getLinksNumber(DOMAIN_STATE_SELECTED) == 0) {
            return;
        }
        mSelectedLinksPreferenceCategory.removeAll();
        final List<String> selectedLinks = IntentPickerUtils.getLinksList(
                mDomainVerificationManager, mPackageName, DOMAIN_STATE_SELECTED);
        for (String host : selectedLinks) {
            generateCheckBoxPreference(mSelectedLinksPreferenceCategory, host);
        }
        mAddLinkPreference.setEnabled(isAddLinksNotEmpty());
    }

    /** Initialize add link preference */
    private void initAddLinkPreference() {
        mAddLinkPreference = findPreference(ADD_LINK_PREF_KEY);
        mAddLinkPreference.setVisible(isAddLinksShown());
        mAddLinkPreference.setEnabled(isAddLinksNotEmpty());
        mAddLinkPreference.setOnPreferenceClickListener(preference -> {
            final int stateNoneLinksNo = getLinksNumber(DOMAIN_STATE_NONE);
            IntentPickerUtils.logd("The number of the state none links: " + stateNoneLinksNo);
            if (stateNoneLinksNo > 0) {
                showProgressDialogFragment();
            }
            return true;
        });
    }

    private boolean isAddLinksNotEmpty() {
        return getLinksNumber(DOMAIN_STATE_NONE) > 0;
    }

    private boolean isAddLinksShown() {
        return (isAddLinksNotEmpty() || getLinksNumber(DOMAIN_STATE_SELECTED) > 0);
    }

    private void showProgressDialogFragment() {
        final Bundle args = new Bundle();
        args.putString(APP_PACKAGE_KEY, mPackageName);
        final ProgressDialogFragment dialogFragment = new ProgressDialogFragment();
        dialogFragment.setArguments(args);
        dialogFragment.showDialog(getActivity().getSupportFragmentManager());
    }

    private void disabledPreference() {
        mMainSwitchPreference.updateStatus(false);
        mMainSwitchPreference.setSelectable(false);
        mMainSwitchPreference.setEnabled(false);
        mMainPreferenceCategory.setVisible(false);
    }

    /** Init OTHER DEFAULTS category */
    private void initOtherDefaultsSection() {
        mOtherDefaultsPreferenceCategory = findPreference(OTHER_DETAILS_PREF_CATEGORY_KEY);
        mOtherDefaultsPreferenceCategory.setVisible(isClearDefaultsEnabled());
        mClearDefaultsPreference = (ClearDefaultsPreference) findPreference(
                CLEAR_DEFAULTS_PREF_KEY);
    }

    private void initFooter() {
        final CharSequence footerText = mContext.getText(R.string.app_launch_footer);
        final FooterPreference footerPreference = (FooterPreference) findPreference(
                FOOTER_PREF_KEY);
        footerPreference.setTitle(footerText);
        // learn more
        footerPreference.setLearnMoreAction(view -> {
            final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(LEARN_MORE_URI));
            mContext.startActivity(intent);
        });
        final String learnMoreText = mContext.getString(
                R.string.footer_learn_more_content_description, getLabelName());
        footerPreference.setLearnMoreText(learnMoreText);
    }

    private String getLabelName() {
        return mContext.getString(R.string.launch_by_default);
    }

    private boolean isClearDefaultsEnabled() {
        final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
        final boolean hasBindAppWidgetPermission =
                appWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName);

        final boolean isAutoLaunchEnabled = AppUtils.hasPreferredActivities(mPm, mPackageName)
                || AppUtils.isDefaultBrowser(mContext, mPackageName)
                || AppUtils.hasUsbDefaults(mUsbManager, mPackageName);

        IntentPickerUtils.logd("isClearDefaultsEnabled hasBindAppWidgetPermission : "
                + hasBindAppWidgetPermission);
        IntentPickerUtils.logd(
                "isClearDefaultsEnabled isAutoLaunchEnabled : " + isAutoLaunchEnabled);
        return (isAutoLaunchEnabled || hasBindAppWidgetPermission);
    }

    private void setDomainVerificationUserSelection(UUID identifier, Set<String> domainSet,
            boolean isEnabled) {
        try {
            mDomainVerificationManager.setDomainVerificationUserSelection(identifier, domainSet,
                    isEnabled);
        } catch (PackageManager.NameNotFoundException e) {
            Log.w(TAG, "addSelectedItems : " + e.getMessage());
        }
    }

    private void generateCheckBoxPreference(PreferenceCategory parent, String title) {
        final LeftSideCheckBoxPreference checkBoxPreference = new LeftSideCheckBoxPreference(
                parent.getContext(), /* isChecked= */ true);
        checkBoxPreference.setTitle(title);
        checkBoxPreference.setOnPreferenceChangeListener(this);
        checkBoxPreference.setKey(UUID.randomUUID().toString());
        parent.addPreference(checkBoxPreference);
    }

    /** get the number of the specify links */
    private int getLinksNumber(@DomainVerificationUserState.DomainState int state) {
        final List<String> linkList = IntentPickerUtils.getLinksList(
                mDomainVerificationManager, mPackageName, state);
        if (linkList == null) {
            return 0;
        }
        return linkList.size();
    }
}
