/*
 * 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.ons;

import android.content.Context;
import android.content.SharedPreferences;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;

import com.android.ons.ONSProfileActivator.Result;
import com.android.ons.ONSProfileDownloader.DownloadRetryResultCode;

/**
 * ONSStats is responsible for collecting and reporting ONS statistics.
 */
public class ONSStats {
    private static final String ONS_ATOM_LOG_FILE = "ons_atom_log_info";
    private static final String KEY_PROVISIONING_RESULT = "_provisioning_result";
    private static final String KEY_DOWNLOAD_RESULT = "_download_result";
    private static final String KEY_RETRY_COUNT = "_retry_count";
    private static final String KEY_DETAILED_ERROR_CODE = "_detailed_error_code";
    private static final String KEY_OPP_CARRIER_ID = "_opportunistic_carrier_id";
    private static final String KEY_PRIMARY_CARRIER_ID = "_primary_sim_carrier_id";
    private final Context mContext;
    private final SubscriptionManager mSubscriptionManager;

    /** Constructor to create instance for ONSStats. */
    public ONSStats(Context context, SubscriptionManager subscriptionManager) {
        mContext = context;
        mSubscriptionManager = subscriptionManager;
    }

    /**
     * It logs the ONS atom with the info passed as ONSStatsInfo. If the information is already
     * logged, it will be skipped.
     *
     * @param info information to be logged.
     * @return returns true if information is logged, otherwise false.
     */
    public boolean logEvent(ONSStatsInfo info) {
        // check if the info needs to be ignored.
        if (ignoreEvent(info)) {
            return false;
        }

        int statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_UNKNOWN;
        if (info.isProvisioningResultUpdated()) {
            switch (info.provisioningResult()) {
                case SUCCESS:
                    statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_SUCCESS;
                    break;
                case ERR_CANNOT_SWITCH_TO_DUAL_SIM_MODE:
                    statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_SWITCH_TO_MULTISIM_FAILED;
                    break;
                case ERR_CARRIER_DOESNT_SUPPORT_CBRS:
                case ERR_AUTO_PROVISIONING_DISABLED:
                    statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_AUTO_PROVISIONING_DISABLED;
                    break;
                case ERR_ESIM_NOT_SUPPORTED:
                case ERR_MULTISIM_NOT_SUPPORTED:
                    statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_DEVICE_NOT_CAPABLE;
                    break;
                case ERR_SINGLE_ACTIVE_OPPORTUNISTIC_SIM:
                case ERR_DUAL_ACTIVE_SUBSCRIPTIONS:
                case ERR_PSIM_NOT_FOUND:
                case ERR_DOWNLOADED_ESIM_NOT_FOUND:
                    statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_ESIM_PROVISIONING_FAILED;
                    break;
                case ERR_WAITING_FOR_INTERNET_CONNECTION:
                case ERR_WAITING_FOR_WIFI_CONNECTION:
                    statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_INTERNET_NOT_AVAILABLE;
                    break;
                case ERR_INVALID_CARRIER_CONFIG:
                    statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_UNRESOLVABLE_ERROR;
                    break;
                default:
                    break;
            }
        } else {
            switch (info.downloadResult()) {
                case ERR_UNRESOLVABLE:
                    statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_UNRESOLVABLE_ERROR;
                    break;
                case ERR_MEMORY_FULL:
                    statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_MEMORY_FULL;
                    break;
                case ERR_INSTALL_ESIM_PROFILE_FAILED:
                    statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_INSTALL_ESIM_PROFILE_FAILED;
                    break;
                case ERR_RETRY_DOWNLOAD:
                    statsCode = OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE__ERROR_CODE__RESULT_CONNECTION_ERROR;
                    break;
                default:
                    break;
            }
        }
        OnsStatsLog.write(
                OnsStatsLog.ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE,
                getSimCarrierId(info.primarySimSubId()),
                info.oppSimCarrierId(),
                info.isWifiConnected(),
                statsCode,
                info.retryCount(),
                info.detailedErrCode());
        updateSharedPreferences(info);
        return true;
    }

    private void updateSharedPreferences(ONSStatsInfo info) {
        SharedPreferences sharedPref =
                mContext.getSharedPreferences(ONS_ATOM_LOG_FILE, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPref.edit();
        if (info.isProvisioningResultUpdated()) {
            editor.putInt(KEY_PROVISIONING_RESULT, info.provisioningResult().ordinal());
            editor.remove(KEY_DOWNLOAD_RESULT);
        } else {
            editor.putInt(KEY_DOWNLOAD_RESULT, info.downloadResult().ordinal());
            editor.remove(KEY_PROVISIONING_RESULT);
        }
        editor.putInt(KEY_PRIMARY_CARRIER_ID, getSimCarrierId(info.primarySimSubId()))
                .putInt(KEY_RETRY_COUNT, info.retryCount())
                .putInt(KEY_OPP_CARRIER_ID, info.oppSimCarrierId())
                .putInt(KEY_DETAILED_ERROR_CODE, info.detailedErrCode())
                .apply();
    }

    private boolean ignoreEvent(ONSStatsInfo info) {
        Result result = info.provisioningResult();
        if (info.isProvisioningResultUpdated()) {
            // Codes are ignored since they are intermediate state of CBRS provisioning check.
            if ((result == Result.DOWNLOAD_REQUESTED)
                    || result == Result.ERR_NO_SIM_INSERTED
                    || result == Result.ERR_DUPLICATE_DOWNLOAD_REQUEST
                    || result == Result.ERR_SWITCHING_TO_DUAL_SIM_MODE) {
                return true;
            }
        }

        SharedPreferences sharedPref =
                mContext.getSharedPreferences(ONS_ATOM_LOG_FILE, Context.MODE_PRIVATE);

        boolean errorCodeUpdated =
                (info.isProvisioningResultUpdated()
                        ? sharedPref.getInt(KEY_PROVISIONING_RESULT, -1) != result.ordinal()
                        : sharedPref.getInt(KEY_DOWNLOAD_RESULT, -1)
                                != info.downloadResult().ordinal());
        boolean carrierIdUpdated =
                sharedPref.getInt(KEY_PRIMARY_CARRIER_ID, -1)
                        != getSimCarrierId(info.primarySimSubId());
        boolean retryCountUpdated = sharedPref.getInt(KEY_RETRY_COUNT, -1) != info.retryCount();
        boolean oppCarrierIdChanged =
                sharedPref.getInt(KEY_OPP_CARRIER_ID, -1) != info.oppSimCarrierId();
        boolean detailedErrorChanged =
                sharedPref.getInt(KEY_DETAILED_ERROR_CODE, -1) != info.detailedErrCode();
        if (!(errorCodeUpdated
                || carrierIdUpdated
                || retryCountUpdated
                || oppCarrierIdChanged
                || detailedErrorChanged)) {
            // Result codes are meant to log on every occurrence. These should not be ignored.
            if (result == Result.SUCCESS
                    || result == Result.ERR_DOWNLOADED_ESIM_NOT_FOUND
                    || info.downloadResult()
                            == DownloadRetryResultCode.ERR_INSTALL_ESIM_PROFILE_FAILED) {
                return false;
            }
            return true;
        }
        return false;
    }

    private int getSimCarrierId(int subId) {
        if (subId == -1) return -1;
        SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
        return (subInfo != null) ? subInfo.getCarrierId() : -1;
    }
}
