/*
 * Created by chenru on 2022/4/25 下午5:05(format year/.
 * Copyright 2015－2022 Sensors Data Inc.
 *
 * 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.sensorsdata.analytics.android.sdk.advert.utils;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.text.TextUtils;

import com.sensorsdata.analytics.android.sdk.SALog;
import com.sensorsdata.analytics.android.sdk.SensorsDataAPI;
import com.sensorsdata.analytics.android.sdk.advert.oaid.SAOaidHelper;
import com.sensorsdata.analytics.android.sdk.core.mediator.SAModuleManager;
import com.sensorsdata.analytics.android.sdk.core.mediator.Modules;
import com.sensorsdata.analytics.android.sdk.data.adapter.DbAdapter;
import com.sensorsdata.analytics.android.sdk.data.adapter.DbParams;
import com.sensorsdata.analytics.android.sdk.plugin.encrypt.SAStoreManager;
import com.sensorsdata.analytics.android.sdk.util.SADataHelper;
import com.sensorsdata.analytics.android.sdk.util.SensorsDataUtils;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ChannelUtils {
    private static final String UTM_SOURCE_KEY = "SENSORS_ANALYTICS_UTM_SOURCE";
    private static final String UTM_MEDIUM_KEY = "SENSORS_ANALYTICS_UTM_MEDIUM";
    private static final String UTM_TERM_KEY = "SENSORS_ANALYTICS_UTM_TERM";
    private static final String UTM_CONTENT_KEY = "SENSORS_ANALYTICS_UTM_CONTENT";
    private static final String UTM_CAMPAIGN_KEY = "SENSORS_ANALYTICS_UTM_CAMPAIGN";
    private static final String SHARED_PREF_UTM = "sensorsdata.utm";
    private static final String SHARED_PREF_CORRECT_TRACK_INSTALLATION = "sensorsdata.correct.track.installation";

    private static HashSet<String> sChannelSourceKeySet = new HashSet<>();
    private static final HashMap<String, String> UTM_MAP = new HashMap<String, String>() {{
        put(UTM_SOURCE_KEY, "$utm_source");
        put(UTM_MEDIUM_KEY, "$utm_medium");
        put(UTM_TERM_KEY, "$utm_term");
        put(UTM_CONTENT_KEY, "$utm_content");
        put(UTM_CAMPAIGN_KEY, "$utm_campaign");
    }};

    private static final List<String> mDeepLinkBlackList = new ArrayList() {{
        add("io.dcloud.PandoraEntryActivity");
    }};

    private static final HashMap<String, String> UTM_LINK_MAP = new HashMap<String, String>() {{
        put(UTM_SOURCE_KEY, "utm_source");
        put(UTM_MEDIUM_KEY, "utm_medium");
        put(UTM_TERM_KEY, "utm_term");
        put(UTM_CONTENT_KEY, "utm_content");
        put(UTM_CAMPAIGN_KEY, "utm_campaign");
    }};

    private static final Map<String, String> LATEST_UTM_MAP = new HashMap<String, String>() {{
        put(UTM_SOURCE_KEY, "$latest_utm_source");
        put(UTM_MEDIUM_KEY, "$latest_utm_medium");
        put(UTM_TERM_KEY, "$latest_utm_term");
        put(UTM_CONTENT_KEY, "$latest_utm_content");
        put(UTM_CAMPAIGN_KEY, "$latest_utm_campaign");
    }};

    private static Map<String, String> sUtmProperties = new HashMap<>();

    private static Map<String, String> sLatestUtmProperties = new HashMap<>();

    public static JSONObject getUtmProperties() {
        if (sUtmProperties.size() > 0) {
            return new JSONObject(sUtmProperties);
        }
        return new JSONObject();
    }

    public static JSONObject getLatestUtmProperties() {
        if (sLatestUtmProperties.size() > 0) {
            return new JSONObject(sLatestUtmProperties);
        }
        return new JSONObject();
    }

    /**
     * 判断是否包含 Utm 属性，trackInstallation 和 trackChannelEvent 中用到.
     *
     * @param properties 属性
     * @return true 包含；false 不包含
     */
    public static boolean hasUtmProperties(JSONObject properties) {
        if (properties == null) {
            return false;
        }
        for (Map.Entry<String, String> entry : UTM_MAP.entrySet()) {
            if (entry != null) {
                if (properties.has(entry.getValue())) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断是否包含 Utm 属性
     *
     * @param parameterNames uri 中的参数名
     * @return true 包含；false 不包含
     */
    public static boolean hasLinkUtmProperties(Set<String> parameterNames) {
        if (parameterNames == null || parameterNames.isEmpty()) {
            return false;
        }
        for (Map.Entry<String, String> entry : UTM_LINK_MAP.entrySet()) {
            if (entry != null) {
                if (parameterNames.contains(entry.getValue())) {
                    return true;
                }
            }
        }
        for (String key : sChannelSourceKeySet) {
            if (!TextUtils.isEmpty(key)) {
                if (sChannelSourceKeySet.contains(key)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获取渠道追踪设置信息
     *
     * @param mContext Context
     * @param androidId androidId
     * @param oaid OAID
     * @param reflectionOAID 反射获取的 OAID
     * @return 拼接的渠道追踪设置信息
     */
    public static String getDeviceInfo(Context mContext, String androidId, String oaid, String reflectionOAID) {
        return String.format("android_id=%s##imei=%s##imei_old=%s##imei_slot1=%s##imei_slot2=%s##imei_meid=%s##mac=%s##oaid=%s##oaid_reflection=%s",
                androidId,
                SensorsDataUtils.getInternationalIdentifier(mContext),
                SensorsDataUtils.getInternationalIdOld(mContext),
                SensorsDataUtils.getSlot(mContext, 0),
                SensorsDataUtils.getSlot(mContext, 1),
                SensorsDataUtils.getEquipmentIdentifier(mContext),
                SensorsDataUtils.getMediaAddress(mContext),
                oaid, reflectionOAID);
    }

    public static void mergeUtmByMetaData(Context context, JSONObject properties) throws JSONException {
        if (properties == null) {
            return;
        }
        for (Map.Entry<String, String> entry : UTM_MAP.entrySet()) {
            if (entry != null) {
                String utmValue = getApplicationMetaData(context, entry.getKey());
                if (!TextUtils.isEmpty(utmValue)) {
                    properties.put(entry.getValue(), utmValue);
                }
            }
        }
    }

    public static void setSourceChannelKeys(String... sourceChannelKeys) {
        sChannelSourceKeySet.clear();
        if (sourceChannelKeys != null && sourceChannelKeys.length > 0) {
            for (String key : sourceChannelKeys) {
                if (!TextUtils.isEmpty(key)) {
                    sChannelSourceKeySet.add(key);
                }
            }
        }
    }

    public static void parseParams(Map<String, String> params) {
        if (params != null && params.size() > 0) {
            for (Map.Entry<String, String> entry : UTM_LINK_MAP.entrySet()) {
                String utmKey = entry.getValue();
                String value = params.get(utmKey);
                if (!TextUtils.isEmpty(value)) {
                    sUtmProperties.put(UTM_MAP.get(entry.getKey()), value);
                    sLatestUtmProperties.put(LATEST_UTM_MAP.get(entry.getKey()), value);
                }
            }
            for (String sourceKey : sChannelSourceKeySet) {
                try {
                    //检测 key 的值,非正常 key 值直接跳过.
                    if (!SADataHelper.assertPropertyKey(sourceKey)) {
                        continue;
                    }
                    String value = params.get(sourceKey);
                    if (!TextUtils.isEmpty(value)) {
                        sUtmProperties.put(sourceKey, value);
                        sLatestUtmProperties.put("_latest_" + sourceKey, value);
                    }
                } catch (Exception e) {
                    SALog.printStackTrace(e);
                }
            }
        }
    }

    /**
     * 初始化 utm 数据,从本地文件
     */
    public static void loadUtmByLocal() {
        try {
            sLatestUtmProperties.clear();
            String channelJson = SAStoreManager.getInstance().getString(SHARED_PREF_UTM, "");
            if (!TextUtils.isEmpty(channelJson)) {
                JSONObject jsonObject = new JSONObject(channelJson);
                for (Map.Entry<String, String> entry : LATEST_UTM_MAP.entrySet()) {
                    String utmKey = entry.getValue();
                    if (jsonObject.has(utmKey)) {
                        sLatestUtmProperties.put(utmKey, jsonObject.optString(utmKey));
                    }
                }
                String latestSourceKey;
                for (String sourceKey : sChannelSourceKeySet) {
                    latestSourceKey = "_latest_" + sourceKey;
                    if (jsonObject.has(latestSourceKey)) {
                        sLatestUtmProperties.put(latestSourceKey, jsonObject.optString(latestSourceKey));
                    }
                }
            }
        } catch (Exception e) {
            SALog.printStackTrace(e);
        }
    }

    /**
     * 清除本地保存的 utm 属性
     */
    public static void clearLocalUtm() {
        try {
            SAStoreManager.getInstance().setString(SHARED_PREF_UTM, "");
        } catch (Exception e) {
            SALog.printStackTrace(e);
        }
    }

    /**
     * 清除内存中的 utm 属性
     */
    public static void clearMemoryUtm() {
        sUtmProperties.clear();
        sLatestUtmProperties.clear();
    }

    /**
     * 清除本地保存和内存中的 utm 属性
     */
    public static void clearUtm() {
        clearMemoryUtm();
        clearLocalUtm();
    }

    /**
     * 需要清除 utm 属性的 JSONObject
     *
     * @param jsonObject 事件属性
     */
    public static void removeDeepLinkInfo(JSONObject jsonObject) {
        try {
            if (jsonObject == null) {
                return;
            }
            String latestKey;
            Iterator<String> keys = jsonObject.keys();
            while (keys.hasNext()) {
                latestKey = keys.next();
                if (latestKey.startsWith("$latest") || latestKey.startsWith("_latest")) {
                    keys.remove();
                }
            }
        } catch (Exception ex) {
            SALog.printStackTrace(ex);
        }
    }

    /**
     * 保存的 utm 属性到本地
     */
    public static void saveDeepLinkInfo() {
        try {
            if (sLatestUtmProperties.size() > 0) {
                SAStoreManager.getInstance().setString(SHARED_PREF_UTM, sLatestUtmProperties.toString());
            } else {
                clearLocalUtm();
            }
        } catch (Exception e) {
            SALog.printStackTrace(e);
        }
    }

    private static String getApplicationMetaData(Context mContext, String metaKey) {
        try {
            ApplicationInfo appInfo = mContext.getApplicationContext().getPackageManager()
                    .getApplicationInfo(mContext.getApplicationContext().getPackageName(),
                            PackageManager.GET_META_DATA);
            String value = appInfo.metaData.getString(metaKey);
            int iValue = -1;
            if (value == null) {
                iValue = appInfo.metaData.getInt(metaKey, -1);
            }
            if (iValue != -1) {
                value = String.valueOf(iValue);
            }
            return value;
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 判断是否需要添加渠道回调事件，如果需要则添加。
     *
     * @param eventName 事件名
     * @param properties 属性
     * @param context Context
     * @return JSONObject
     */
    public static JSONObject checkOrSetChannelCallbackEvent(String eventName, JSONObject properties, Context context) {
        if (properties == null) {
            properties = new JSONObject();
        }
        try {
            boolean isFirst = isFirstChannelEvent(eventName);
            properties.put("$is_channel_callback_event", isFirst);
            if (context != null && !ChannelUtils.hasUtmProperties(properties)) {
                ChannelUtils.mergeUtmByMetaData(context, properties);
            }
            properties.put("$channel_device_info", "1");
        } catch (JSONException e) {
            SALog.printStackTrace(e);
        }
        return properties;
    }

    /**
     * 是否是首次触发的渠道事件
     *
     * @param eventName 事件名称
     * @return 是否是首次触发
     */
    public static boolean isFirstChannelEvent(String eventName) {
        boolean isDefault = SensorsDataAPI.getConfigOptions().getStorePlugins() == null || SensorsDataAPI.getConfigOptions().getStorePlugins().isEmpty();
        String secretEventName = isDefault ? eventName : (String) SAModuleManager.getInstance().invokeModuleFunction(Modules.Encrypt.MODULE_NAME, Modules.Encrypt.METHOD_ENCRYPT_AES, eventName);
        boolean isFirst = DbAdapter.getInstance().isFirstChannelEvent(new String[]{secretEventName, eventName});
        if (isFirst) {
            DbAdapter.getInstance().addChannelEvent(secretEventName);
        }
        return isFirst;
    }

    /**
     * meta 中是否包含 Utm 属性
     *
     * @param context Context
     * @return meta 中是否包含 Utm 属性
     */
    public static boolean hasUtmByMetaData(Context context) {
        if (context == null) {
            return false;
        }
        for (Map.Entry<String, String> entry : UTM_MAP.entrySet()) {
            if (entry != null) {
                String utmValue = getApplicationMetaData(context, entry.getKey());
                if (!TextUtils.isEmpty(utmValue)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 是否获取到设备信息
     *
     * @param context Context
     * @param androidId AndroidID
     * @param oaid 移动联通设备标识
     * @return 是否获取到设备信息（macAddress 除外）
     */
    public static boolean isGetDeviceInfo(Context context, String androidId, String oaid) {
        try {
            return !TextUtils.isEmpty(androidId) ||
                    !TextUtils.isEmpty(oaid) ||
                    !TextUtils.isEmpty(SensorsDataUtils.getInternationalIdentifier(context)) ||
                    !TextUtils.isEmpty(SensorsDataUtils.getInternationalIdOld(context)) ||
                    !TextUtils.isEmpty(SensorsDataUtils.getSlot(context, 0)) ||
                    !TextUtils.isEmpty(SensorsDataUtils.getSlot(context, 1)) ||
                    !TextUtils.isEmpty(SensorsDataUtils.getEquipmentIdentifier(context));
        } catch (Exception e) {
            SALog.printStackTrace(e);
        }
        return false;
    }


    /**
     * 是否触发过 trackInstallation 事件
     *
     * @return 是否触发过 trackInstallation 事件
     */
    public static boolean isTrackInstallation() {
        try {
            return SAStoreManager.getInstance().isExists(SHARED_PREF_CORRECT_TRACK_INSTALLATION);
        } catch (Exception e) {
            SALog.printStackTrace(e);
        }
        return false;
    }

    /**
     * 是否正确触发过 trackInstallation 事件
     *
     * @return 是否正确触发过 trackInstallation 事件
     */
    public static boolean isCorrectTrackInstallation() {
        try {
            return SAStoreManager.getInstance().getBool(SHARED_PREF_CORRECT_TRACK_INSTALLATION, false);
        } catch (Exception e) {
            SALog.printStackTrace(e);
        }
        return false;
    }

    /**
     * 保存 trackInstallation 事件是否正确触发标识
     *
     * @param isCorrectTrackInstallation trackInstallation 事件是否正确触发标识
     */
    public static void saveCorrectTrackInstallation(boolean isCorrectTrackInstallation) {
        try {
            SAStoreManager.getInstance().setBool(SHARED_PREF_CORRECT_TRACK_INSTALLATION, isCorrectTrackInstallation);
        } catch (Exception e) {
            SALog.printStackTrace(e);
        }
    }

    /**
     * 设备信息是否匹配
     *
     * @param context Context
     * @param deviceInfo 设备信息字符串
     * @return 是否匹配
     */
    public static boolean checkDeviceInfo(Context context, String deviceInfo) {
        if (context == null || TextUtils.isEmpty(deviceInfo)) {
            return false;
        }
        String[] codes = deviceInfo.split("##");
        Map<String, String> deviceMaps = new HashMap<>();
        if (codes.length == 0) {
            return false;
        }
        for (String code : codes) {
            String[] keyValue = code.trim().split("=");
            if (keyValue.length != 2) {
                continue;
            }
            deviceMaps.put(keyValue[0], keyValue[1]);
        }
        if (deviceMaps.isEmpty()) {
            return false;
        }
        return (deviceMaps.containsKey("oaid")//防止都为 null 返回 true
                && TextUtils.equals(deviceMaps.get("oaid"), SAOaidHelper.getOpenAdIdentifier(context))) ||
                (deviceMaps.containsKey("imei") &&
                        TextUtils.equals(deviceMaps.get("imei"), SensorsDataUtils.getInternationalIdentifier(context))) ||
                (deviceMaps.containsKey("imei_old") &&
                        TextUtils.equals(deviceMaps.get("imei_old"), SensorsDataUtils.getInternationalIdOld(context))) ||
                (deviceMaps.containsKey("imei_slot1") &&
                        TextUtils.equals(deviceMaps.get("imei_slot1"), SensorsDataUtils.getSlot(context, 0))) ||
                (deviceMaps.containsKey("imei_slot2") &&
                        TextUtils.equals(deviceMaps.get("imei_slot2"), SensorsDataUtils.getSlot(context, 1))) ||
                (deviceMaps.containsKey("imei_meid") &&
                        TextUtils.equals(deviceMaps.get("imei_meid"), SensorsDataUtils.getEquipmentIdentifier(context))) ||
                (deviceMaps.containsKey("android_id") &&
                        TextUtils.equals(deviceMaps.get("android_id"), SensorsDataUtils.getIdentifier(context))) ||
                (deviceMaps.containsKey("mac") &&
                        TextUtils.equals(deviceMaps.get("mac"), SensorsDataUtils.getMediaAddress(context)));
    }

    /**
     * deeplink 不解析 Activity 名单(包含子类）
     *
     * @param activity activity
     * @return 是否包含在黑名单中
     */
    public static boolean isDeepLinkBlackList(Activity activity) {
        if (activity != null) {
            for (String activityName : mDeepLinkBlackList) {
                try {
                    Class<?> clazz = Class.forName(activityName);
                    if (clazz.isAssignableFrom(activity.getClass())) {
                        return true;
                    }
                } catch (Exception e) {
                    SALog.printStackTrace(e);
                }
            }
        }
        return false;
    }

    /**
     * 判断是否存在该属性
     *
     * @return 是否存在该标记位
     */
    public static boolean isExistRequestDeferredDeeplink() {
        return SAStoreManager.getInstance().isExists(DbParams.PersistentName.REQUEST_DEFERRER_DEEPLINK);
    }

    /**
     * 是否允许请求 DeferredDeeplink
     *
     * @return 是否允许请求 DeferredDeeplink
     */
    public static boolean isRequestDeferredDeeplink() {
        return SAStoreManager.getInstance().getBool(DbParams.PersistentName.REQUEST_DEFERRER_DEEPLINK, true);
    }

    /**
     * 修改请求 DeferredDeeplink 标记位
     *
     * @param isRequest 修改标记位
     */
    public static void commitRequestDeferredDeeplink(boolean isRequest) {
        SAStoreManager.getInstance().setBool(DbParams.PersistentName.REQUEST_DEFERRER_DEEPLINK, isRequest);
    }
}
