package com.duiba.tuia.youtui.web.service.impl;
/**
 * Created by chengdeman .
 * 17/9/13 .
 */

import cn.com.duiba.tuia.activity.center.api.dto.ActivityOrderDto;
import cn.com.duiba.tuia.activity.center.api.log.InnerLog;
import cn.com.duiba.tuia.activity.center.api.remoteservice.RemoteHuayaConsumerService;
import cn.com.duiba.wolf.utils.UrlUtils;
import cn.com.tuia.advert.enums.AdvertOrderJsonKeyEnum;
import cn.com.tuia.advert.enums.ChargeTypeEnum;
import cn.com.tuia.advert.model.QueryAdvertReq;
import cn.com.tuia.advert.model.QueryAdvertRsp;
import cn.com.tuia.advert.service.IEngineService;
import com.alibaba.dubbo.common.json.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dianping.cat.Cat;
import com.duiba.tuia.youtui.web.constant.Constants;
import com.duiba.tuia.youtui.web.constant.ErrorCode;
import com.duiba.tuia.youtui.web.exception.ActivityException;
import com.duiba.tuia.youtui.web.log.InnerLogService;
import com.duiba.tuia.youtui.web.model.req.AdvertEffectReq;
import com.duiba.tuia.youtui.web.model.req.InnerLogReq;
import com.duiba.tuia.youtui.web.service.*;
import com.duiba.tuia.youtui.web.tool.RequestTool;
import com.duiba.tuia.youtui.web.tool.SHA1;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;

import static com.duiba.tuia.youtui.web.constant.ErrorCode.E0110006;

/**
 * 落地页 回传，广告转化回传逻辑处理类
 *
 * @author chengdeman
 * @create 2017-09-13 上午11:49
 **/
@Service
public class LogServiceImpl extends BaseCacheService implements LogService{

    public static final String BIZ_ADVERT_LAND_LOG = "land_log";

    /** The Constant log. */
    private static final Logger log = LoggerFactory.getLogger(LogServiceImpl.class);

    @Resource
    private IEngineService iEngineService;
    @Resource
    private CommonService commonService;
    @Resource
    private RocketMQService rocketMQService;
    @Resource
    private RemoteHuayaConsumerService remoteHuayaConsumerService;
    @Resource
    private ActivityOrderService activityOrderService;


    @Override
    public void landPageLog(InnerLogReq req, HttpServletRequest request) throws ActivityException {
        String json = req.getJson();
        Integer type = req.getType();

        // 2 查询广告信息
        JSONObject data = JSONObject.parseObject(json);

        //检查cid，tuiaId
        this.checkCidAndTuiaId(data);

        String tuiaId = data.getString(Constants.TUIA_ID);
        String referrer = data.getString(Constants.VALUENAME.REFERRER);
        String url = data.getString(Constants.VALUENAME.URL);


        // 3 校验请求有效性(300s内重复请求无效)
        Boolean effective = null;
        try {
            effective = this.commonService.checkEffective(300, BIZ_ADVERT_LAND_LOG, type.toString(),
                    tuiaId, referrer, url);
        } catch (Exception e) {
            logger.warn("请求有效性检查异常", e);
        }
        // 4 如果有效, 执行打日志操作（有效性检查异常 也打日志）
        if (effective == null || effective) {
            if(StringUtils.isEmpty(data.getString(Constants.CONSUMER_ID))) {
                data.put(Constants.CONSUMER_ID, getCid(tuiaId));
            }
            this.jssdkLog(data, request, tuiaId, type);
        }
    }

    @Override
    public void allLandPageLog(InnerLogReq req, HttpServletRequest request) throws ActivityException {
        String json = req.getJson();
        Integer type = req.getType();

        // 2 查询广告信息
        JSONObject data = JSONObject.parseObject(json);

        //检查cid，tuiaId
        this.checkCidAndTuiaId(data);

        String tuiaId = data.getString(Constants.TUIA_ID);
        String referrer = data.getString(Constants.VALUENAME.REFERRER);
        String url = data.getString(Constants.VALUENAME.URL);


        // 不管是否有效都要打印日志     －－20180124  －cdm  49 曝光， 50 转化
        data.put("ua", RequestTool.getUA(RequestTool.getUserAgent(request)));
        data.put("requestType", "jssdk");
        InnerLogService.log(new InnerLog().group(1).type(type).put("json", data).toJSONString());
    }

    /*
    * 落地页日志记录 需查询广告相关信息并记录
    */
    private void jssdkLog(JSONObject data, HttpServletRequest request, String tuiaId, int type) {
        Long cid = data.getLong(Constants.CONSUMER_ID);

        // 2. 获取referrer
        String refer = data.getString(Constants.VALUENAME.REFERRER);

        //1. 生成referrerType
        this.setReferrerType(data, request, refer);

        setDataMap(data, tuiaId, cid);

        // 4 记录日志
        data.put("ua", RequestTool.getUA(RequestTool.getUserAgent(request)));
        data.put("requestType", "jssdk");
        InnerLogService.log(new InnerLog().group(1).type(type).put("json", data).toJSONString());


        // 5 发送消息给哪吒系统
        sendMessageNeZha(data, type);

    }

    /**
     * 向nezha 发消息
     * @param data 数据
     * @param type 类型
     */
    private void sendMessageNeZha(JSONObject data, int type) {
        if (type == 8 && ChargeTypeEnum.TYPE_CPA.getDesc().equals(data.getString("chargeType"))) {
            Long advertId = data.getLong("advertId");

            if (advertId != null) {
                JSONObject json = new JSONObject();
                json.put("adid", advertId);
                json.put("packageId", data.getLong("adSpecId"));
                Long appId = data.getLong(AdvertOrderJsonKeyEnum.KEY_APP_ID.getCode());
                json.put("appId", appId == null ? 0 : appId);
                Long slotId = data.getLong(Constants.SLOTID);
                json.put(Constants.SLOTID, slotId == null ? 0 : slotId);
                Long activityId = data.getLong(AdvertOrderJsonKeyEnum.KEY_ACTIVITY_ID.getCode());
                Integer activitySource = data.getInteger(AdvertOrderJsonKeyEnum.KEY_ACTIVITY_SOURCE.getCode());
                puActivityId(json, activityId, activitySource);

                try {
                    Cat.logMetricForCount("广告转化日志");
                } catch (Exception e) {
                    log.error(Constants.CAT_ERROR, e);
                }

                this.rocketMQService.sendCPAAdvertMQ(json);
            }
        }
    }

    private void puActivityId(JSONObject json, Long activityId, Integer activitySource) {
        Long aId = activityId == null ? 0L : activityId;
        Integer as = activitySource == null ? -1 : activitySource;
        json.put("activityId", as + "_" + aId);
    }

    /**
     * set 广告信息
     * @param data 数据
     * @param tuiaId 订单id
     * @param cid 用户id
     */
    private void setDataMap(JSONObject data, String tuiaId, Long cid) {
        if (StringUtils.isNotBlank(tuiaId)) {
            //2. 根据tuiaId和cid查询广告信息
            QueryAdvertRsp rsp = this.queryAdvert(cid, tuiaId, data);
            if (rsp != null) {
                if (rsp.getLogExtMap() != null) {
                    data.putAll(rsp.getLogExtMap());
                }
                data.put(Constants.SLOTID, rsp.getSlotId());
                data.put("advertId", rsp.getAdvertId());
                data.put("result", rsp.isResult());
                data.put("materialId", rsp.getMaterialId());
                data.put("adSpecId", rsp.getAdSpecId());
            }

        }

    }

    /*
     * 查询广告信息
     */
    private QueryAdvertRsp queryAdvert(Long consumerId, String tuiaId, JSONObject data) {
        QueryAdvertReq req = new QueryAdvertReq();
        req.setConsumerId(consumerId);
        req.setOrderId(tuiaId);
        req.setShowType(2);

        QueryAdvertRsp rsp = null;

        if(tuiaId.startsWith(Constants.THE_PREFIX_DIRECT)){
            /**
             * 广告直投时的需要记录霍尔果斯的主题类型
             * @modify lichao 2018-04-24
             */
            try {
                ActivityOrderDto activityOrderDto = this.activityOrderService.selectByIdAndConsumerId(tuiaId.substring(Constants.THE_PREFIX_DIRECT.length()), consumerId);
                if(activityOrderDto!=null){
                    data.put("effectiveMainType",activityOrderDto.getCouponData("effectiveMainType"));
                    data.put("advertId", activityOrderDto.getCouponData("advertId"));
                }
            } catch (Exception e) {
                logger.error("转化返回orderId查询失败,tuiaId="+tuiaId,e);
            }
            return null;

        }else if(tuiaId.startsWith(//直投广告，直投链接 不调接口查询
                Constants.THE_PREFIX_LINK)){
            return null;
        }

        try {
            rsp = this.iEngineService.queryAdvert(req);
        } catch (Exception e) {
            logger.error("查询广告券信息出错,tuiaId = " + tuiaId, e);
        }
        return rsp;
    }

    /*
     * 设置referrerType 逻辑： 前端传来的json中会包含referrer参数， 判断referrer的域名是否是与当前系统域名匹配 0：不是，1：是,2:没传referrer
     */
    private void setReferrerType(JSONObject data, HttpServletRequest request, String refer) {
        // 1. 获取接口请求url中的域名
        String domain = request.getServerName();
        String referrerDomain;
        Integer referrerType = null;


        // 3.1 referrer为空 则 referrerType =2
        if (StringUtils.isBlank(refer)) {
            referrerType = 2;
        } else {
            try {
                referrerDomain = new URL(URLDecoder.decode(refer, "utf-8")).getHost();// 获取referrer url的域名

                if (domain.equalsIgnoreCase(referrerDomain)) {
                    // 3.2.1 referrer域名与请求域名 相等，referrerType =1
                    referrerType = 1;
                } else {
                    // 3.2.2 referrer域名与请求域名 不等，referrerType =1
                    referrerType = 0;
                }
            } catch (MalformedURLException e) {
                logger.error("parse referrer error", e);
            } catch (UnsupportedEncodingException e) {
                logger.error("unsupport encoding", e);
            }
        }

        data.put("referrerType", referrerType);
    }

    /*
     * 前端传来的json中,如果没有tuiaId,则从url中获取
     */
    private void checkCidAndTuiaId(JSONObject data) throws ActivityException {
        try {
            String url = data.getString(Constants.VALUENAME.URL);
            //tuiaId 改为 oId
            String tuiaId = getTuiaId(data);

            if (StringUtils.isNotBlank(url)) {
                Map<String, String> params = UrlUtils.uRLRequest(url);
                if (StringUtils.isBlank(tuiaId)) {
                    data.put(Constants.TUIA_ID,params.get(Constants.THE_ORDER_ID));
                }
                if (StringUtils.isBlank(data.getString(Constants.CONSUMER_ID))) {
                    data.put(Constants.CONSUMER_ID, params.get(Constants.THE_CONSUMER_ID));
                }
            }
        } catch (Exception e) {
            logger.error("", e);
        }

    }

    private Long getCid(String tuiaId) throws ActivityException {
        if(StringUtils.isNotBlank(tuiaId) && tuiaId.startsWith(Constants.THE_PREFIX_ORDERID)) {
            String orderId = tuiaId.substring(4);
            //校验订单号
            if (!checkOrderId(orderId)) {
                logger.warn("订单号异常。不符合订单号规则orderId is error   and order id is " + orderId);
                throw new ActivityException(ErrorCode.E0120007.geteCode(),ErrorCode.E0120007.geteDesc());
            }
            // 3.通过订单ID查询cid
            Long cid = activityOrderService.selectConsumerIdByOrderId(orderId);
            if(cid != null && cid != 0l) {
                return cid;
            }
        }
        return null;
    }

    /**
     * checkOrderId:(这里用一句话描述这个方法的作用). <br/>
     *
     * @param orderId
     * @return
     */
    private Boolean checkOrderId(String orderId) throws ActivityException {
        // 1.订单号非空和长度校验
        if (null == orderId || orderId.length() < 5 || orderId.length() > 19) {
            logger.error("非法订单号，error  orderId is " + orderId);
            return false;
        }

        // 2.截取orderId 获取后四位，作为表后缀校验
        String orderIdSuffix = orderId.substring(orderId.length() - 4);
        Long orederTb = Long.valueOf(orderIdSuffix);

        return orederTb < 1024;
    }

    private String getTuiaId(JSONObject data) {
        String tuiaId = data.getString(Constants.O_ID);
        if (StringUtils.isBlank(tuiaId)) {
            tuiaId = data.getString(Constants.TUIA_ID);
        } else {
            data.put(Constants.TUIA_ID, tuiaId);
        }
        return tuiaId;
    }



    /**
     * 广告转化效果上报
     * @param request
     * @param advertEffectReq  广告转化传入对象
     * @return
     * @throws ActivityException
     */
    @Override
    public JSONObject advertEffect(HttpServletRequest request, AdvertEffectReq advertEffectReq) throws ActivityException {
        JSONObject jsonObject = new JSONObject();

        if(advertEffectReq.getSubType()>0){
            advertEffectReq.setType(61);
        }

        // 1.参数非空判断
        if(checkAdvertEffectParam(jsonObject,advertEffectReq)){
            // 2.判断是否已经回传过（a_tuiaid对比，1h内只记录一条，统计时也要做数据清洗，tuiaid重复的剔除）

            // 3.判断是否签名正确  暂时不需要签名
           /* checkSignature(jsonObject,advertEffectReq) */
            // 4.记录日志类型
            advertEffectReq.setRequestType("api");

            // 5 校验请求有效性(300s内重复请求无效)
            Boolean effective = null;
            try {
                effective = (61==advertEffectReq.getType())||this.commonService.checkEffective(300, BIZ_ADVERT_LAND_LOG, String.valueOf(advertEffectReq.getType()),
                        advertEffectReq.getA_tuiaId());
            } catch (Exception e) {
                logger.warn("请求有效性检查异常", e);
            }

            // 6 如果有效, 发送哪吒消息
            JSONObject data = getJsonObject(advertEffectReq, effective);

            // 记录日志
            String ua = request.getHeader("user-agent");
            if (ua != null && ua.length() > 500) {
                ua = ua.substring(0, 499);
            }
            if (ua != null) {
                data.put("user_agent", ua);
            }
            String referer =  request.getHeader(Constants.VALUENAME.REFERRER);
            if(referer != null) {
                data.put(Constants.VALUENAME.REFERRER, referer);
            }

            data.put("tuiaId",advertEffectReq.getA_tuiaId());
            if(data.get("cid")==null) {
                data.put("cid", advertEffectReq.getA_cid());
            }
            data.put("ua", RequestTool.getUA(RequestTool.getUserAgent(request)));
            InnerLogService.innerJsonObject(1,advertEffectReq.getType(),data);

            // 构造成功返回
            jsonObject.put(Constants.VALUENAME.RECORD, ErrorCode.E0000000.geteCode());
            jsonObject.put(Constants.VALUENAME.RE_DESC,ErrorCode.E0000000.geteDesc());
        }



        return jsonObject;
    }

    public JSONObject getJsonObject(AdvertEffectReq advertEffectReq, Boolean effective) {
        JSONObject data = null;
        try {
            data = JSONObject.parseObject(JSON.json(advertEffectReq));
            if (effective == null || effective) {
                // 6.1.根据tuiaId获取cid，获取失败从参数中获取
                Long cid = getCid(advertEffectReq.getA_tuiaId());
                if (cid != null) {
                    advertEffectReq.setA_cid(cid);
                }
                this.apiLog(data, advertEffectReq.getA_cid(), advertEffectReq.getA_tuiaId(), advertEffectReq.getType());
            }
        }catch (Exception e){
            logger.warn("发送nezha消息异常", e);
        }
        return data;
    }

    /*
    * 落地页api日志记录 需查询广告相关信息并记录
    */
    private void apiLog(JSONObject data, Long cid, String tuiaId, int type) {
        // 根据tuiaId构造data信息
        setDataMap(data, tuiaId, cid);

        // 5 发送消息给哪吒系统
        sendMessageNeZha(data, type);

    }


    /**
     * 校验广告回传必填参数
     * @param jsonObject
     * @param advertEffectReq
     * @return
     */
    private   Boolean  checkAdvertEffectParam(JSONObject jsonObject,AdvertEffectReq advertEffectReq){

        // 参数对象非空判断
        if(null == advertEffectReq) {
            jsonObject.put(Constants.VALUENAME.RECORD, ErrorCode.E0110001.geteCode());
            jsonObject.put(Constants.VALUENAME.RE_DESC,ErrorCode.E0110001.geteDesc());

            return Boolean.FALSE;
        }

        // tuiaid非空判断
        if(null == advertEffectReq.getA_tuiaId() || StringUtils.isBlank(advertEffectReq.getA_tuiaId())){
            jsonObject.put(Constants.VALUENAME.RECORD, ErrorCode.E0110002.geteCode());
            jsonObject.put(Constants.VALUENAME.RE_DESC,ErrorCode.E0110002.geteDesc());

            return Boolean.FALSE;
        }


        // type非空判断
        if(advertEffectReq.getType() != 8 && advertEffectReq.getType() != 7 &&  advertEffectReq.getType() != 61 ){
            jsonObject.put(Constants.VALUENAME.RECORD, ErrorCode.E0110007.geteCode());
            jsonObject.put(Constants.VALUENAME.RE_DESC,ErrorCode.E0110007.geteDesc());

            return Boolean.FALSE;
        }


        return true;
    }


    /**
     * 校验签名是否正确
     * @param jsonObject
     * @param advertEffectReq
     * @return
     */

    public Boolean checkSignature(JSONObject jsonObject,AdvertEffectReq advertEffectReq){
        ArrayList<String> list=new ArrayList<>();
        list.add(advertEffectReq.getA_tuiaId());
        list.add(String.valueOf(advertEffectReq.getA_cid()));
        list.add(advertEffectReq.getA_timeStamp());
        list.add(Constants.TUIA_ADVERT_EFFECT_KEY);
        Collections.sort(list);

        // 4.获取token参数进行sha1加密
        String signStr = list.get(0)+list.get(1)+list.get(2)+list.get(3);

        try {
            String sigin = SHA1.sha1(signStr);

            if(!sigin.equals(advertEffectReq.getSignature())){
                jsonObject.put(Constants.VALUENAME.RECORD, E0110006.geteCode());
                jsonObject.put(Constants.VALUENAME.RE_DESC, E0110006.geteDesc());
                return Boolean.FALSE;
            }
            return Boolean.TRUE;
        }catch ( Exception e){
            jsonObject.put(Constants.VALUENAME.RECORD, E0110006.geteCode());
            jsonObject.put(Constants.VALUENAME.RE_DESC, E0110006.geteDesc());
            return Boolean.FALSE;
        }
    }

}
