/*
 * Copyright (C) 2022 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.ondevicepersonalization.services;

import static android.adservices.ondevicepersonalization.OnDevicePersonalizationPermissions.MODIFY_ONDEVICEPERSONALIZATION_STATE;

import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__API_CALLBACK_ERROR;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__API_REMOTE_EXCEPTION;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ON_DEVICE_PERSONALIZATION_ERROR;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__ODP;

import android.adservices.ondevicepersonalization.Constants;
import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationConfigService;
import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationConfigServiceCallback;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.pm.PackageManager;
import android.ondevicepersonalization.IOnDevicePersonalizationSystemService;
import android.ondevicepersonalization.IOnDevicePersonalizationSystemServiceCallback;
import android.ondevicepersonalization.OnDevicePersonalizationSystemServiceManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;

import com.android.modules.utils.build.SdkLevel;
import com.android.ondevicepersonalization.internal.util.LoggerFactory;
import com.android.ondevicepersonalization.services.data.user.RawUserData;
import com.android.ondevicepersonalization.services.data.user.UserDataCollector;
import com.android.ondevicepersonalization.services.data.user.UserPrivacyStatus;
import com.android.ondevicepersonalization.services.statsd.errorlogging.ClientErrorLogger;

import java.util.Objects;
import java.util.concurrent.Executor;

/**
 * ODP service that modifies and persists ODP enablement status
 */
public class OnDevicePersonalizationConfigServiceDelegate
        extends IOnDevicePersonalizationConfigService.Stub {
    private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
    private static final String TAG = "OnDevicePersonalizationConfigServiceDelegate";
    private final Context mContext;
    private static final Executor sBackgroundExecutor =
            OnDevicePersonalizationExecutors.getBackgroundExecutor();
    private static final int SERVICE_NOT_IMPLEMENTED = 501;

    public OnDevicePersonalizationConfigServiceDelegate(Context context) {
        mContext = context;
    }

    @Override
    @RequiresPermission(MODIFY_ONDEVICEPERSONALIZATION_STATE)
    public void setPersonalizationStatus(boolean enabled,
                                     @NonNull IOnDevicePersonalizationConfigServiceCallback
                                             callback) {
        if (getGlobalKillSwitch()) {
            throw new IllegalStateException("Service skipped as the API flag is turned off.");
        }

        // Verify caller's permission
        if (mContext.checkCallingPermission(MODIFY_ONDEVICEPERSONALIZATION_STATE)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException(
                    "Permission denied: " + MODIFY_ONDEVICEPERSONALIZATION_STATE);
        }
        Objects.requireNonNull(callback);

        sBackgroundExecutor.execute(
                () -> {
                    try {
                        UserPrivacyStatus userPrivacyStatus = UserPrivacyStatus.getInstance();

                        boolean oldStatus = userPrivacyStatus.isPersonalizationStatusEnabled();
                        userPrivacyStatus.setPersonalizationStatusEnabled(enabled);
                        boolean newStatus = userPrivacyStatus.isPersonalizationStatusEnabled();

                        if (oldStatus == newStatus) {
                            sendSuccess(callback);
                            return;
                        }

                        // Rollback all user data if personalization status changes
                        RawUserData userData = RawUserData.getInstance();
                        UserDataCollector userDataCollector =
                                UserDataCollector.getInstance(mContext);
                        userDataCollector.clearUserData(userData);
                        userDataCollector.clearMetadata();

                        // TODO(b/302018665): replicate system server storage to T devices.
                        if (!SdkLevel.isAtLeastU()) {
                            userPrivacyStatus.setPersonalizationStatusEnabled(enabled);
                            sendSuccess(callback);
                            return;
                        }
                        // Persist in the system server for U+ devices
                        OnDevicePersonalizationSystemServiceManager systemServiceManager =
                                mContext.getSystemService(
                                        OnDevicePersonalizationSystemServiceManager.class);
                        // Cannot find system server on U+.
                        if (systemServiceManager == null) {
                            sendError(callback, SERVICE_NOT_IMPLEMENTED);
                            return;
                        }
                        IOnDevicePersonalizationSystemService systemService =
                                systemServiceManager.getService();
                        // The system service is not ready.
                        if (systemService == null) {
                            sendError(callback, SERVICE_NOT_IMPLEMENTED);
                            return;
                        }
                        try {
                            systemService.setPersonalizationStatus(
                                    enabled,
                                    new IOnDevicePersonalizationSystemServiceCallback.Stub() {
                                        @Override
                                        public void onResult(Bundle bundle) throws RemoteException {
                                            userPrivacyStatus.setPersonalizationStatusEnabled(
                                                    enabled);
                                            callback.onSuccess();
                                        }

                                        @Override
                                        public void onError(int errorCode) throws RemoteException {
                                            callback.onFailure(errorCode);
                                        }
                                    });
                        } catch (RemoteException re) {
                            sLogger.e(TAG + ": Unable to send result to the callback.", re);
                            ClientErrorLogger.getInstance()
                                    .logErrorWithExceptionInfo(
                                            re,
                                            AD_SERVICES_ERROR_REPORTED__ERROR_CODE__API_REMOTE_EXCEPTION,
                                            AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__ODP);
                        }
                    } catch (Exception e) {
                        sLogger.e(TAG + ": Failed to set personalization status.", e);
                        ClientErrorLogger.getInstance()
                                .logErrorWithExceptionInfo(
                                        e,
                                        AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ON_DEVICE_PERSONALIZATION_ERROR,
                                        AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__ODP);
                        sendError(callback, Constants.STATUS_INTERNAL_ERROR);
                    }
                });
    }

    private void sendSuccess(
            @NonNull IOnDevicePersonalizationConfigServiceCallback callback) {
        try {
            callback.onSuccess();
        } catch (RemoteException e) {
            sLogger.e(TAG + ": Callback error", e);
            ClientErrorLogger.getInstance()
                    .logErrorWithExceptionInfo(
                            e,
                            AD_SERVICES_ERROR_REPORTED__ERROR_CODE__API_CALLBACK_ERROR,
                            AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__ODP);
        }
    }

    private void sendError(
            @NonNull IOnDevicePersonalizationConfigServiceCallback callback, int errorCode) {
        try {
            callback.onFailure(errorCode);
        } catch (RemoteException e) {
            sLogger.e(TAG + ": Callback error", e);
            ClientErrorLogger.getInstance()
                    .logErrorWithExceptionInfo(
                            e,
                            AD_SERVICES_ERROR_REPORTED__ERROR_CODE__API_CALLBACK_ERROR,
                            AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__ODP);
        }
    }

    private boolean getGlobalKillSwitch() {
        long origId = Binder.clearCallingIdentity();
        boolean globalKillSwitch = FlagsFactory.getFlags().getGlobalKillSwitch();
        Binder.restoreCallingIdentity(origId);
        return globalKillSwitch;
    }
}
