package com.fshark.overseas.advert.modle;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import com.fshark.overseas.advert.util.Constants;
import com.fshark.overseas.advert.util.DateUtils;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.apache.flink.shaded.curator4.com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author zhouml [zhouml@kingnet.com]
 * @date 2019-03-01
 */
@Data
public class AppsFlyerEvent implements Serializable {

    private static final Logger logger = LoggerFactory.getLogger(AppsFlyerEvent.class);

    /**
     * 事件 createrole/sdk login/fast login
     */
    private String event;

    /**
     * in-app-event
     * install
     */
    private String event_type;

    /**
     * country_code
     * install
     */
    private String region;

    /**
     * appsflyer_device_id
     * install
     */
    private String appsflyer_device_id;

    /**
     * 日志时间(ms)
     */
    private Date event_time;

    /**
     * json event_value
     */
    private String event_value;

    /**
     * 日志时区 +0900
     */
    private String time_zone;

    private String select_time_zone;

    /**
     * 用户唯一标识 appsflyer_device_id
     */
    private String uid;

    /**
     * 设备唯一标识
     * ios --> idfa
     * android --> advertising
     */
    private String did;

    /**
     * 平台
     * android | ios
     */
    private String platform;

    /**
     * 媒体平台
     * media_source &&
     * <p>
     * googleadwords_int | Facebook Ad
     */
    private String media_source;

    /**
     * 游戏ID bundle_id
     */
    private String game_id;

    /**
     * 游戏对应名称 --> 새 천하를 열다 |  새 천하를 열다
     */
    private String app_name;

    /**
     * App Id --> com.edgames.newworld | id1280877546
     */
    private String app_id;

    /**
     * 广告计划ID
     */
    private String campaign_id;

    /**
     * 广告计划
     */
    private String campaign;

    /**
     * 广告组(计划)ID
     */
    private String ad_set_id;

    /**
     * 广告组(计划)
     */
    private String ad_set;

    /**
     * 广告ID
     */
    private String ad_id;

    /**
     * 广告
     */
    private String ad;

    /**
     * 渠道
     */
    private String channel;

    /**
     * 订单编号
     */
    private String orderno;

    /**
     * 货币单位 currency
     */
    private String currency;

    /**
     * 收益 revenue_in_selected_currency
     */
    private Double revenue;

    /**
     * ip地址
     */
    private String ip;

    /**
     * 错误的原因，日志解析错误 | 某些计算字段缺失
     */
    private String error_reason;

    /**
     * 客服端ID 上报用于???
     */
    private String customer_user_id;

    /**
     * 再营销数据
     */
    private Boolean is_retargeting = false;



    private static List<String> validation_fields =
            Lists.newArrayList(
                    "event_time_selected_timezone", "platform", "bundle_id", "event_type", "event_name",
                    "appsflyer_device_id", "imei", "idfa", "media_source"
            );


    public static String nvl(String first, String defaultValue) {
        if (StringUtils.isNotBlank(first)) {
            return first;
        } else {
            return defaultValue;
        }
    }

    public static String nvl(String first, String second, String defaultValue) {
        if (StringUtils.isNotBlank(first)) {
            return first;
        } else if (StringUtils.isNotBlank(second)) {
            return second;
        } else {
            return defaultValue;
        }
    }

    /**
     * 如果是应用内事件， 则取event_name
     * xxx  创角有一个特殊的情况需要考虑 創角(ios)发送的
     * <p>
     * 如果是激活事件， 则取event_type
     *
     * @param eventType
     * @param eventName
     * @return
     */
    private static String getEvent(JSONObject jsonObject, String eventType, String eventName) {
        return jsonObject.containsKey("api_version") &&
                "2.0".equals(jsonObject.getString("api_version")) ?
                getEventV2(eventName) :
                getEventV1(eventType, eventName);
    }

    private static String getEventV1(String eventType, String eventName) {
        if ("in-app-event".equals(eventType)) {
            return eventName.equals("創角") ? EventTypeEnum.CREATE_ROLE.getName() : eventName.toLowerCase();
        } else if (EventTypeEnum.ACTIVATION.getName().equals(eventType)) {
            return eventType.toLowerCase();
        } else {
            return Constants.DEFAULT_FIELD_VALUE;
        }
    }

    private static String getEventV2(String eventName) {
        return "創角".equals(eventName) ? EventTypeEnum.CREATE_ROLE.getName() : eventName.toLowerCase();
    }

    /**
     * 如果是android, 则使用advertising_id
     * 如果是ios, 则使用idfa
     *
     * @param jsonObject
     * @param appsFlyerEvent
     */
    private static void setDidByType(AppsFlyerEvent appsFlyerEvent, JSONObject jsonObject) {
        String advertisingId = jsonObject.getString("advertising_id");
        String idfa = jsonObject.getString("idfa");
        if (StringUtils.isBlank(idfa)) {
            idfa = jsonObject.getString("idfv");
        }
        String platform = jsonObject.getString("platform");

        String did;
        if (PlatformEnum.IOS.getName().equals(platform)) {
            did = nvl(idfa, Constants.DEFAULT_FIELD_VALUE);
        } else if (PlatformEnum.Android.getName().equals(platform)) {
            did = nvl(advertisingId, Constants.DEFAULT_FIELD_VALUE);
        } else {
            did = Constants.DEFAULT_FIELD_VALUE;
        }
        appsFlyerEvent.setDid(did);
    }

    /**
     * 设置广告相关的属性
     * <p>
     * FB的广告是有正常的三级的
     * 而非FB的(谷歌的, SinoInteractive)广告只有一级
     *
     * @param appsFlyerEvent
     * @param jsonObject
     */
    private static void setAdByType(AppsFlyerEvent appsFlyerEvent, JSONObject jsonObject) {
        String media_source = getMediaSource(jsonObject);

        String campaign_id;
        String campaign;

        String ad_set_id;
        String ad_set;

        String ad_id;
        String ad;

        if (media_source.equals("facebook ads")) {
            String fbCampaign = jsonObject.getString("fb_campaign_id");
            String fbCampaignName = jsonObject.getString("fb_campaign_name");

            String fbAdSet = jsonObject.getString("fb_adset_id");
            String fbAdSetName = jsonObject.getString("fb_adset_name");

            String fbAd = jsonObject.getString("fb_adgroup_id");
            String fbAdName = jsonObject.getString("fb_adgroup_name");

            campaign_id = nvl(fbCampaign, Constants.DEFAULT_FIELD_VALUE);
            campaign = nvl(fbCampaignName, Constants.DEFAULT_FIELD_VALUE);

            ad_set_id = nvl(fbAdSet, Constants.DEFAULT_FIELD_VALUE);
            ad_set = nvl(fbAdSetName, Constants.DEFAULT_FIELD_VALUE);

            ad_id = nvl(fbAd, Constants.DEFAULT_FIELD_VALUE);
            ad = nvl(fbAdName, Constants.DEFAULT_FIELD_VALUE);
        } else {
            String nonFBCampaign = jsonObject.getString("af_c_id");
            String nonFBCampaignName = jsonObject.getString("campaign");

            String nonFBAdSet = jsonObject.getString("af_adset_id");
            String nonFBAdSetName = jsonObject.getString("af_adset");

            String nonFBAd = jsonObject.getString("af_ad_id");
            String nonFBAdName = jsonObject.getString("af_ad");

            campaign_id = nvl(nonFBCampaign, Constants.DEFAULT_FIELD_VALUE);
            campaign = nvl(nonFBCampaignName, Constants.DEFAULT_FIELD_VALUE);

            ad_set_id = nvl(nonFBAdSet, Constants.DEFAULT_FIELD_VALUE);
            ad_set = nvl(nonFBAdSetName, Constants.DEFAULT_FIELD_VALUE);

            ad_id = nvl(nonFBAd, Constants.DEFAULT_FIELD_VALUE);
            ad = nvl(nonFBAdName, Constants.DEFAULT_FIELD_VALUE);
        }
        //有的渠道Campaign有值但是campaign_id没有值,做这个兼容设置
        String modifiedCampaign = "None".equals(campaign) ? Constants.DEFAULT_FIELD_VALUE : campaign;
        if (!Constants.DEFAULT_FIELD_VALUE.equals(modifiedCampaign) && Constants.DEFAULT_FIELD_VALUE.equals(campaign_id)){
            campaign_id = modifiedCampaign;
        }
        appsFlyerEvent.setCampaign_id(campaign_id);

        appsFlyerEvent.setCampaign(modifiedCampaign);

        appsFlyerEvent.setAd_set_id(ad_set_id);
        appsFlyerEvent.setAd_set(ad_set);

        appsFlyerEvent.setAd_id(ad_id);
        appsFlyerEvent.setAd(ad);
    }

    private static String getMediaSource(JSONObject jsonObject) {
        String media_source = nvl(
                jsonObject.getString("media_source"),
                jsonObject.getString("agency"),
                Constants.DEFAULT_FIELD_VALUE
        ).toLowerCase();

        return media_source;
    }

    /**
     * appsflyer日志解析相关注意事项
     * <p>
     * ## 解析方面
     * <p>
     * + 如果是安装|激活，则需要从event_type(install)中取值
     * + 其它事件(in-app event), 则需要从event_name中取值
     * <p>
     * ## 验证方面
     * + event_time_selected_timezone 不能为空
     * + event_type and event_name 不能同时为空
     * + bundle_id(game_id) 不能为空
     * + platform不能为空
     *
     * @param line
     * @return
     */
    public static AppsFlyerEvent parse(String line) {
        try {
            JSONObject jsonObject = JSON.parseObject(line);

            compatibleVersion(jsonObject);

            String eventTimeWithZone = jsonObject.getString("event_time_selected_timezone");
            String selected_timezone = jsonObject.getString("selected_timezone");
            String platform = jsonObject.getString("platform");
            String gameId = jsonObject.getString("bundle_id");

            String eventType = jsonObject.getString("event_type");
            String eventName = jsonObject.getString("event_name");
            Boolean is_retargeting = Boolean.parseBoolean(jsonObject.getString("is_retargeting"));
//            String af_channel = String.valueOf(jsonObject.getOrDefault("af_channel", Constants.DEFAULT_FIELD_VALUE));

            // TODO 这个地方如果对于同一账户，不同设备，会有问题，因为这个是appflyer 针对每个安装自动生成的唯一的ID
            String uid = String.valueOf(jsonObject.getOrDefault("appsflyer_device_id", Constants.DEFAULT_FIELD_VALUE));

            // 因国外政策限制，所以imei基本拿不到, 需要使用 advertising_id
            String advertisingId = jsonObject.getString("advertising_id");
            String idfa = jsonObject.getString("idfa");
            if (StringUtils.isBlank(idfa)) {
                idfa = jsonObject.getString("idfv");
            }
            String appsflyerDeviceId = Objects.toString(jsonObject.getString("appsflyer_device_id"), "-1");

            // 获取来源，默认韩国
            String region = Objects.toString(jsonObject.getString("country_code"), "-1");
            if (StringUtils.isBlank(region)) {
                region = "-1";
            }

            String media_source = getMediaSource(jsonObject);

            if (
                    StringUtils.isBlank(eventTimeWithZone) ||
                            StringUtils.isBlank(platform) ||
                            StringUtils.isBlank(gameId) ||
                            (StringUtils.isBlank(eventType) && StringUtils.isBlank(eventName)) ||
                            (StringUtils.isBlank(uid) && StringUtils.isBlank(advertisingId) && StringUtils.isBlank(idfa)) ||
                            StringUtils.isBlank(media_source)
            ) {
                logger.error(String.format("Invalid log, possible invalid fields (%s), line: %s", String.join(",", validation_fields), line));
                return null;
            }

            AppsFlyerEvent appsFlyerEvent = new AppsFlyerEvent();

            String event = getEvent(jsonObject, eventType, eventName);
            appsFlyerEvent.setEvent_type(eventType);

            String[] timeZoneParts = eventTimeWithZone.split("\\+");
            Date eventTime = DateUtils.formatHMSSAsDate(timeZoneParts[0]);
            // 如果有错，直接视为错误; +0900
            String timeZone = eventTimeWithZone.substring(23);

            String ip = jsonObject.getString("ip");
            String currency = jsonObject.getString("selected_currency");
            Double revenue = jsonObject.getDouble("revenue_in_selected_currency");

            appsFlyerEvent.setEvent(event);
            appsFlyerEvent.setEvent_time(eventTime);
            appsFlyerEvent.setTime_zone(timeZone);
            appsFlyerEvent.setSelect_time_zone(selected_timezone == null ? "+0900" : selected_timezone);
            appsFlyerEvent.setRegion(region);

            appsFlyerEvent.setUid(uid);
            setDidByType(appsFlyerEvent, jsonObject);
            appsFlyerEvent.setAppsflyer_device_id(appsflyerDeviceId);

            appsFlyerEvent.setPlatform(platform.toLowerCase());
            appsFlyerEvent.setGame_id(gameId);
            appsFlyerEvent.setMedia_source(media_source);

            setAdByType(appsFlyerEvent, jsonObject);

            appsFlyerEvent.setCurrency(currency);
            appsFlyerEvent.setRevenue(revenue);
            appsFlyerEvent.setIp(ip);
            appsFlyerEvent.setIs_retargeting(is_retargeting);
            String af_channel = jsonObject.getString("af_channel");
            if (StringUtils.isBlank(af_channel)) {
                af_channel = Constants.DEFAULT_FIELD_VALUE;
            }
            appsFlyerEvent.setChannel(af_channel);

            appsFlyerEvent.setApp_name(
                    Objects.toString(jsonObject.getString("app_name"), Constants.DEFAULT_FIELD_VALUE)
            );

            appsFlyerEvent.setEvent_value(
                    Objects.toString(jsonObject.getString("event_value"), Constants.DEFAULT_FIELD_VALUE)
            );

            appsFlyerEvent.setApp_id(
                    Objects.toString(jsonObject.getString("app_id"), Constants.DEFAULT_FIELD_VALUE)
            );

            appsFlyerEvent.setCustomer_user_id(
                    Objects.toString(jsonObject.getString("customer_user_id"), Constants.DEFAULT_FIELD_VALUE)
            );

            return appsFlyerEvent;
        } catch (Exception e) {
            logger.error("Failed to parse log: " + line, e);
        }

        return null;
    }

    public String toJson() {
        return JSON.toJSONString(this);
    }

    public static AppsFlyerEvent fromJson(String json) {
        return JSON.parseObject(json, AppsFlyerEvent.class);
    }

    /**
     * 兼容 v1 v2,
     * 主要逻辑： 对于v1 版本的日志不做变更， 对于 v2 版本的日志将其包装转换为 v1 的字段
     * 字段变更： see https://support.appsflyer.com/hc/en-us/articles/207034356-Using-Push-API-V2-0-real-time-raw-data#migrate-v10v20
     *
     * @param jsonObject
     */
    private static void compatibleVersion(JSONObject jsonObject) {
        if (jsonObject.containsKey("api_version") &&
                "2.0".equals(jsonObject.getString("api_version"))) {
            // v2 版本的日志， 进行降版本操作， facebook 媒体的日志需要进行转换
            if (getMediaSource(jsonObject).equals("facebook ads")) {
                setAttribute(jsonObject, "fb_campaign_id", "af_c_id");
                setAttribute(jsonObject, "fb_campaign_name", "campaign");

                setAttribute(jsonObject, "fb_adset_id", "af_adset_id");
                setAttribute(jsonObject, "fb_adset_name", "af_adset");

                setAttribute(jsonObject, "fb_adgroup_id", "af_ad_id");
                setAttribute(jsonObject, "fb_adgroup_name", "af_ad");
            }

            // 转换一些被改名的字段
            setAttribute(jsonObject, "appsflyer_device_id", "appsflyer_id");
            setAttribute(jsonObject, "agency", "af_prt");
        }
    }

    private static void setAttribute(JSONObject jsonObject, String v1Col, String v2Col) {
        jsonObject.put(v1Col, jsonObject.get(v2Col));
    }

    public boolean isInstallEvent(){
        return EventTypeEnum.ROLE_LOGIN.equals(event);
    }

}