/*
 * 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.settings.biometrics.activeunlock;

import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;

import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.BasePreferenceController.AvailabilityStatus;

/** Utilities for active unlock details shared between Security Settings and Safety Center. */
public class ActiveUnlockStatusUtils {

    /** The flag to determining whether active unlock in settings is enabled. */
    public static final String CONFIG_FLAG_NAME = "active_unlock_in_settings";

    /** Flag value that represents the layout for unlock intent should be used. */
    public static final String UNLOCK_INTENT_LAYOUT = "unlock_intent_layout";

    /** Flag value that represents the layout for biometric failure should be used. */
    public static final String BIOMETRIC_FAILURE_LAYOUT = "biometric_failure_layout";

    private static final String ACTIVE_UNLOCK_PROVIDER = "active_unlock_provider";
    private static final String ACTIVE_UNLOCK_TARGET = "active_unlock_target";

    private static final String TAG = "ActiveUnlockStatusUtils";

    private final Context mContext;
    private final ContentResolver mContentResolver;

    public ActiveUnlockStatusUtils(@NonNull Context context) {
        mContext = context;
        mContentResolver = mContext.getContentResolver();
    }

    /** Returns whether the active unlock settings entity should be shown. */
    public boolean isAvailable() {
        return getAvailability() == BasePreferenceController.AVAILABLE;
    }

    /**
     * Returns whether the active unlock layout with the unlock on intent configuration should be
     * used.
     */
    public boolean useUnlockIntentLayout() {
        return isAvailable();
    }

    /**
     *
     * Returns whether the active unlock layout with the unlock on biometric failure configuration
     * should be used.
     */
    public boolean useBiometricFailureLayout() {
        return false;
    }

    /**
     * Returns the authority used to fetch dynamic active unlock content.
     */
    @Nullable
    public String getAuthority() {
        final String authority = Settings.Secure.getString(
                mContext.getContentResolver(), ACTIVE_UNLOCK_PROVIDER);
        if (authority == null) {
            Log.i(TAG, "authority not set");
            return null;
        }
        final ProviderInfo provider = mContext.getPackageManager().resolveContentProvider(
                authority, PackageManager.ComponentInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY));
        if (provider == null) {
            Log.i(TAG, "could not find provider");
            return null;
        }
        if (authority.equals(provider.authority) && isSystemApp(provider)) {
            return authority;
        }
        Log.e(TAG, "authority not valid");
        return null;
    }

    private static boolean isSystemApp(ComponentInfo componentInfo) {
        final ApplicationInfo applicationInfo = componentInfo.applicationInfo;
        if (applicationInfo == null) {
            Log.e(TAG, "application info is null");
            return false;
        }
        return applicationInfo.isSystemApp();
    }

    /**
     * Returns the intent used to launch the active unlock activity.
     */
    @Nullable
    public Intent getIntent() {
        final String targetAction = Settings.Secure.getString(
                mContentResolver, ACTIVE_UNLOCK_TARGET);
        if (targetAction == null) {
            Log.i(TAG, "Target action not set");
            return null;
        }
        final Intent intent = new Intent(targetAction);
        final ActivityInfo activityInfo = intent.resolveActivityInfo(
                mContext.getPackageManager(), PackageManager.MATCH_ALL);
        if (activityInfo == null) {
            Log.e(TAG, "Target activity not found");
            return null;
        }
        if (!isSystemApp(activityInfo)) {
            Log.e(TAG, "Target application is not system");
            return null;
        }
        Log.i(TAG, "Target application is valid");
        return intent;
    }

    /** Returns the availability status of the active unlock feature. */
    @AvailabilityStatus
    int getAvailability() {
        if (!Utils.hasFingerprintHardware(mContext) && !Utils.hasFaceHardware(mContext)) {
            return BasePreferenceController.UNSUPPORTED_ON_DEVICE;
        }
        if (getAuthority() != null && getIntent() != null) {
            return BasePreferenceController.AVAILABLE;
        }
        return BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
    }

    /**
     * Returns the title of the combined biometric settings entity when active unlock is enabled.
     */
    public String getTitleForActiveUnlock() {
        final boolean faceAllowed = Utils.hasFaceHardware(mContext);
        final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
        return mContext.getString(getTitleRes(faceAllowed, fingerprintAllowed));
    }

    @StringRes
    private static int getTitleRes(boolean isFaceAllowed, boolean isFingerprintAllowed) {
        if (isFaceAllowed && isFingerprintAllowed) {
            return R.string.security_settings_biometric_preference_title;
        } else if (isFaceAllowed) {
            return R.string.security_settings_face_preference_title;
        } else if (isFingerprintAllowed) {
            return R.string.security_settings_fingerprint_preference_title;
        } else {
            // Default to original summary, but this case should never happen.
            return R.string.security_settings_biometric_preference_title;
        }
    }

    /**
     * Returns the intro of the combined biometric settings entity when active unlock is enabled.
     */
    public String getIntroForActiveUnlock() {
        final boolean faceAllowed = Utils.hasFaceHardware(mContext);
        final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
        if (isAvailable()) {
            int introRes = getIntroRes(faceAllowed, fingerprintAllowed);
            return introRes == 0 ? "" : mContext.getString(introRes);
        }
        return mContext.getString(R.string.biometric_settings_intro);
    }

    @StringRes
    private static int getIntroRes(boolean isFaceAllowed, boolean isFingerprintAllowed) {
        if (isFaceAllowed && isFingerprintAllowed) {
            return R.string.biometric_settings_intro_with_activeunlock;
        } else if (isFaceAllowed) {
            return R.string.biometric_settings_intro_with_face;
        } else if (isFingerprintAllowed) {
            return R.string.biometric_settings_intro_with_fingerprint;
        } else {
            return 0;
        }
    }

    /**
     * Returns the summary of the unlock device entity when active unlock is enabled.
     */
    public String getUnlockDeviceSummaryForActiveUnlock() {
        final boolean faceAllowed = Utils.hasFaceHardware(mContext);
        final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);

        return mContext.getString(getUnlockDeviceSummaryRes(faceAllowed, fingerprintAllowed));
    }

    @StringRes
    private static int getUnlockDeviceSummaryRes(
            boolean isFaceAllowed, boolean isFingerprintAllowed) {
        if (isFaceAllowed && isFingerprintAllowed) {
            return R.string.biometric_settings_use_face_fingerprint_or_watch_preference_summary;
        } else if (isFaceAllowed) {
            return R.string.biometric_settings_use_face_or_watch_preference_summary;
        } else if (isFingerprintAllowed) {
            return R.string.biometric_settings_use_fingerprint_or_watch_preference_summary;
        } else {
            return R.string.biometric_settings_use_watch_preference_summary;
        }
    }

    /**
     * Returns the summary of the active unlock preference when biometrics are needed to set up the
     * feature.
     */
    @Nullable
    public String getSummaryWhenBiometricSetupRequired() {
        final boolean faceAllowed = Utils.hasFaceHardware(mContext);
        final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);

        int summaryRes = getSetupBiometricRes(faceAllowed, fingerprintAllowed);
        return summaryRes == 0 ? null : mContext.getString(summaryRes);
    }

    @StringRes
    private static int getSetupBiometricRes(boolean faceAllowed, boolean fingerprintAllowed) {
        if (faceAllowed && fingerprintAllowed) {
            return R.string.security_settings_activeunlock_require_face_fingerprint_setup_title;
        } else if (faceAllowed) {
            return R.string.security_settings_activeunlock_require_face_setup_title;
        } else if (fingerprintAllowed) {
            return R.string.security_settings_activeunlock_require_fingerprint_setup_title;
        } else {
            return 0;
        }
    }

    /**
     * Returns the preference title of how to use biometrics when active unlock is enabled.
     */
    public String getUseBiometricTitleForActiveUnlock() {
        final boolean faceAllowed = Utils.hasFaceHardware(mContext);
        final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);

        return mContext.getString(getUseBiometricTitleRes(faceAllowed, fingerprintAllowed));
    }

    @StringRes
    private static int getUseBiometricTitleRes(
            boolean isFaceAllowed, boolean isFingerprintAllowed) {
        if (isFaceAllowed && isFingerprintAllowed) {
            return R.string.biometric_settings_use_face_fingerprint_or_watch_for;
        } else if (isFaceAllowed) {
            return R.string.biometric_settings_use_face_or_watch_for;
        } else if (isFingerprintAllowed) {
            return R.string.biometric_settings_use_fingerprint_or_watch_for;
        } else {
            return R.string.biometric_settings_use_watch_for;
        }
    }

    private static String getFlagState() {
        return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_REMOTE_AUTH, CONFIG_FLAG_NAME);
    }
}