package com.bailuntec.job;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bailuntec.domain.enumerate.CurrencyType;
import com.bailuntec.domain.pojo.AmazonAdAuth;
import com.bailuntec.domain.pojo.AmazonAdProduct;
import com.bailuntec.domain.pojo.AmazonAdReportLocationResult;
import com.bailuntec.domain.pojo.AmazonAdSuccessResult;
import com.bailuntec.domain.entity.DcBaseCompanyAccount;
import com.bailuntec.domain.entity.DcBaseFinanceAmazonAdProduct;
import com.bailuntec.domain.entity.JobAccountLog;
import com.bailuntec.domain.entity.JobPointLog;
import com.bailuntec.domain.example.DcBaseCompanyAccountExample;
import com.bailuntec.domain.example.DcBaseFinanceAmazonAdProductExample;
import com.bailuntec.domain.example.JobAccountLogExample;
import com.bailuntec.mapper.DcBaseCompanyAccountMapper;
import com.bailuntec.mapper.DcBaseFinanceAmazonAdProductMapper;
import com.bailuntec.mapper.JobAccountLogMapper;
import com.bailuntec.support.CallBailunSystem;
import com.bailuntec.support.PointJob;
import com.bailuntec.utils.ExceptionUtil;
import com.bailuntec.utils.OkHttpUtil;
import com.bailuntec.utils.PropertiesUtil;
import com.bailuntec.utils.SessionUtil;
import com.dangdang.ddframe.job.api.ShardingContext;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.zip.GZIPInputStream;

/**
 * 拿到AMAZON平台SKU维度的广告费用
 */
@Slf4j
public class AmazonAdProductJob extends PointJob {
    private final int platformId = 15;
    private PropertiesUtil propertiesUtil = PropertiesUtil.getInstance("const");
    private OkHttpClient client = OkHttpUtil.getInstance();
    private final long SLEEP_WAIT = 8000;
    @Override
    public void executeJob(ShardingContext shardingContext, JobPointLog jobPointLog) {
        int totalPage = getCount();//分片即将处理的账号总数
        try {
            JobAccountLogMapper jobAccountLogMapper = SessionUtil.getSession().getMapper(JobAccountLogMapper.class);
            /*
             * 根据不同分片
             * 再分页去拿不同的账号执行任务
             */
            int pageSize = totalPage % shardingContext.getShardingTotalCount() == 0?  totalPage / shardingContext.getShardingTotalCount() : totalPage / shardingContext.getShardingTotalCount() + 1;
            //拿到账号集合, TOKEN到用的时候再拿, 不放集合里
            List<JobAccountLog> listByPage = jobAccountLogMapper.getListByPage(platformId,pageSize * shardingContext.getShardingItem(), pageSize);
            if (listByPage != null && listByPage.size() > 0) {
                for (JobAccountLog jobAccountLog : listByPage) {
                    log.warn("分片: " + shardingContext.getShardingItem() + " 账号: " + jobAccountLog);
                    if (jobAccountLog.getId() == null) { //在任务表无记录
                        BeanUtils.copyProperties(jobAccountLog, jobPointLog);
                        jobAccountLog.setId(null);
                    }
                    callAmazonAdApi(jobAccountLog);
                    if (jobAccountLog.getId() == null) { //在任务表无记录
                        jobAccountLogMapper.insertSelective(jobAccountLog);
                    } else {
                        jobAccountLog.setBjModified(LocalDateTime.now());
                        jobAccountLogMapper.updateByExampleSelective(jobAccountLog, JobAccountLogExample.newAndCreateCriteria().andAccountIdEqualTo(jobAccountLog.getAccountId()).andJobNameEqualTo(jobAccountLog.getJobName()).example());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.warn("Amaozom定时同步广告接口错误",e);
        }  finally {
            SessionUtil.closeSession();
        }
    }

    /**
     * 调用Amazon广告系列接口
     * @param jobAccountLog
     */
    public void callAmazonAdApi(JobAccountLog jobAccountLog) {
        //通过company_id和account_id拿到最新的授权
        DcBaseCompanyAccount dcBaseCompanyAccount = getToken(jobAccountLog);
        //授权信息存在才去调接口
        if (StringUtils.isNoneBlank(dcBaseCompanyAccount.getAmazonAdAuthJson())) {
            AmazonAdAuth amazonAdAuth = JSON.parseObject(dcBaseCompanyAccount.getAmazonAdAuthJson(), AmazonAdAuth.class);
            //1. 先获取ReportId
            String reportId = postReportId(jobAccountLog, amazonAdAuth,dcBaseCompanyAccount.getSiteEn());
            //2. 再获取Report下载地址
            if (StringUtils.isNoneBlank(reportId)) {
                try {
                    Thread.sleep(SLEEP_WAIT);
                } catch (InterruptedException e) {
                    jobAccountLog.setMessage("线程异常"+ ExceptionUtil.transform(e));
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
                String reportLocationUrl = getReportLocation(reportId, jobAccountLog, amazonAdAuth,dcBaseCompanyAccount.getSiteEn());
                //3. 下载Report报告
                if (StringUtils.isNoneBlank(reportLocationUrl)) {
                    handleReport(reportLocationUrl, jobAccountLog, amazonAdAuth);
                } else {
                    if (StringUtils.isBlank(jobAccountLog.getMessage())) {
                        jobAccountLog.setMessage("返回的Report下载地址为空");
                    }
                }
            } else {
                if (StringUtils.isBlank(jobAccountLog.getMessage())) {
                    jobAccountLog.setMessage("返回的ReportId为空");
                }
            }
        } else {
            jobAccountLog.setMessage("授权信息为空");
        }
    }

    /**
     * 如果param为null,
     * 就POST _URL
     * 否则GET _URL
     * https://advertising.amazon.com/API/docs/v2/reference/product_ads
     * @param siteEn
     * @param param
     * @return
     */
    private String switchSiteUrl(String siteEn, String param) {
        switch (siteEn.toUpperCase()) {
            case "UK":
            case "FR":
            case "IT":
            case "ES":
            case "DE":
                return getURL("EU_URL", param);
            case "AU":
            case "JP":
            case "IN":
                return getURL("FE_URL", param);
            case "US":
            case "CA":
            case "MX":
            default:
                return getURL("NA_URL", param);
            }

    }

    private String getURL(String siteUrl, String param) {
        if (StringUtils.isBlank(param)) {
            return  propertiesUtil.getPropertyAsString(siteUrl) + propertiesUtil.getPropertyAsString("POST_PRODUCT_ADS_REPORT_ID");
        }
        return propertiesUtil.getPropertyAsString(siteUrl) + propertiesUtil.getPropertyAsString("GET_PRODUCT_ADS_REPORT_LOCATION");
    }

    private void handleReport(String reportLocationUrl, JobAccountLog jobAccountLog, AmazonAdAuth amazonAdAuth) {
        Response response = null;
        try{
            Request request = new Request.Builder()
                    .url(reportLocationUrl)
                    .addHeader("Authorization",  amazonAdAuth.getAccessToken())
                    .addHeader("Amazon-Advertising-API-ClientId", amazonAdAuth.getClientId())
                    .addHeader("Amazon-Advertising-API-Scope", amazonAdAuth.getProfileId())
                    .addHeader("Content-Type", "application/json")
                    .build();
            response = client.newCall(request).execute();
            if (response.isSuccessful()) {
                GZIPInputStream gzin = new GZIPInputStream(response.body().byteStream());
                InputStreamReader isr = new InputStreamReader(gzin, "utf8");
                BufferedReader bufferedReader = new BufferedReader(isr);
                StringBuffer sb = new StringBuffer();
                String str = null;
                try {
                    while ((str = bufferedReader.readLine()) != null) {
                        sb.append(str);
                    }
                    List<AmazonAdProduct> amazonAdProductList = JSON.parseArray(sb.toString(), AmazonAdProduct.class);
                    if (amazonAdProductList != null && amazonAdProductList.size() > 0) {
                        DcBaseFinanceAmazonAdProduct dcBaseFinanceAmazonAdProduct = new DcBaseFinanceAmazonAdProduct();
                        DcBaseFinanceAmazonAdProductMapper mapper = SessionUtil.getSession().getMapper(DcBaseFinanceAmazonAdProductMapper.class);
                        for (AmazonAdProduct amazonAdvert : amazonAdProductList) {
                            if (BigDecimal.ZERO.compareTo(amazonAdvert.getCost()) != 0) {
                                BeanUtils.copyProperties(dcBaseFinanceAmazonAdProduct, amazonAdvert);
                                dcBaseFinanceAmazonAdProduct.setAccountId(jobAccountLog.getAccountId());
                                dcBaseFinanceAmazonAdProduct.setCompanyId(jobAccountLog.getCompanyId());
                                dcBaseFinanceAmazonAdProduct.setReportDate(jobAccountLog.getStartTime().toLocalDate());
                                dcBaseFinanceAmazonAdProduct.setExchangeRate(CallBailunSystem.getExchangeRate(dcBaseFinanceAmazonAdProduct.getCurrency(), CurrencyType.CNY.value(), jobAccountLog.getStartTime()));
                                dcBaseFinanceAmazonAdProduct.setExchangeRateUsd(CallBailunSystem.getExchangeRate(dcBaseFinanceAmazonAdProduct.getCurrency(), CurrencyType.USD.value(), jobAccountLog.getStartTime()));
                                dcBaseFinanceAmazonAdProduct.setBjModifyTime(LocalDateTime.now());
                                DcBaseFinanceAmazonAdProductExample example = DcBaseFinanceAmazonAdProductExample.newAndCreateCriteria().andCompanyIdEqualTo(dcBaseFinanceAmazonAdProduct.getCompanyId()).andAccountIdEqualTo(dcBaseFinanceAmazonAdProduct.getAccountId()).andCampaignIdEqualTo(dcBaseFinanceAmazonAdProduct.getCampaignId()).andAdGroupIdEqualTo(dcBaseFinanceAmazonAdProduct.getAdGroupId()).andSkuEqualTo(dcBaseFinanceAmazonAdProduct.getSku()).andReportDateEqualTo(dcBaseFinanceAmazonAdProduct.getReportDate()).example();
                                int i = mapper.updateByExampleSelective(dcBaseFinanceAmazonAdProduct,example);
                                if (i == 0) {
                                    mapper.insertSelective(dcBaseFinanceAmazonAdProduct);
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                   e.printStackTrace();
                   jobAccountLog.setMessage("解析Gzip格式的Report错误:" + ExceptionUtil.transform(e));
                } finally {
                    bufferedReader.close();
                    isr.close();
                    gzin.close();
                }
                /*
                 * 今天只能拿到昨天的广告费, 不然不完整
                 * 如果开始时间比今天0点晚, 就设为22点(或者你随便减几个小时都可以)
                 */
                LocalDateTime zeroTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
                LocalDateTime overStartTime = jobAccountLog.getEndTime().isAfter(zeroTime) ? zeroTime.minusHours(8) : jobAccountLog.getEndTime();
                jobAccountLog.setStartTime(overStartTime);
                jobAccountLog.setEndTime(jobAccountLog.getStartTime().plusDays(jobAccountLog.getIntervalTime()).isAfter(LocalDateTime.now()) ? LocalDateTime.now() : jobAccountLog.getEndTime().plusDays(jobAccountLog.getIntervalTime()));
                jobAccountLog.setMessage("执行成功");
            } else {
                String s = response.body().string();
                log.warn("下载报告不成功:" + s);
                jobAccountLog.setMessage(s);
            }
        }catch (Exception e){
            e.printStackTrace();
            jobAccountLog.setMessage("调用获取AmazonADReportLocation错误:" + ExceptionUtil.transform(e));
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }

    private String getReportLocation(String reportId, JobAccountLog jobAccountLog, AmazonAdAuth amazonAdAuth, String siteEn) {
        Response response = null;
        try{
            Request request = new Request.Builder()
                    .url(switchSiteUrl(siteEn, reportId) + reportId)
                    .addHeader("Authorization",  amazonAdAuth.getAccessToken())
                    .addHeader("Amazon-Advertising-API-ClientId", amazonAdAuth.getClientId())
                    .addHeader("Amazon-Advertising-API-Scope", amazonAdAuth.getProfileId())
                    .addHeader("Content-Type", "application/json")
                    .build();
            response = client.newCall(request).execute();
            if (response.isSuccessful()) {
                AmazonAdReportLocationResult reportLocationResult = JSON.parseObject(response.body().string(), AmazonAdReportLocationResult.class);
                if (reportLocationResult.getStatus().equals("SUCCESS")) {
                    return reportLocationResult.getLocation();
                }
                try {
                    Thread.sleep(SLEEP_WAIT);
                } catch (InterruptedException e) {
                    jobAccountLog.setMessage("线程异常"+ ExceptionUtil.transform(e));
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
                return  getReportLocation(reportId, jobAccountLog, amazonAdAuth, siteEn);
            } else {
                String s = response.body().string();
                log.warn("获取报告下载地址不成功:" + s);
                jobAccountLog.setMessage(s);
            }
        }catch (IOException e){
            e.printStackTrace();
            jobAccountLog.setMessage("调用获取AmazonADReportLocation错误:" + ExceptionUtil.transform(e));
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return  null;
    }

    private String postReportId(JobAccountLog jobAccountLog, AmazonAdAuth amazonAdAuth, String siteEn) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("reportDate", jobAccountLog.getStartTime().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
        jsonObject.put("metrics", propertiesUtil.getPropertyAsString("METRICS"));
        MediaType mediaType = MediaType.parse("application/json");
        Response response = null;
        try{
            RequestBody body = RequestBody.create(mediaType, jsonObject.toJSONString());
            Request request = new Request.Builder()
                    .url(switchSiteUrl(siteEn, null))
                    .post(body)
                    .addHeader("Authorization",  amazonAdAuth.getAccessToken())
                    .addHeader("Amazon-Advertising-API-ClientId", amazonAdAuth.getClientId())
                    .addHeader("Amazon-Advertising-API-Scope", amazonAdAuth.getProfileId())
                    .addHeader("Content-Type", "application/json")
                    .build();
            response = client.newCall(request).execute();
            String string = response.body().string();
            if (response.isSuccessful()) {
                AmazonAdSuccessResult amazonAdSuccessResult = JSON.parseObject(string, AmazonAdSuccessResult.class);
                return amazonAdSuccessResult.getReportId();
            } else {
                jobAccountLog.setMessage(string);
            }
        }catch (IOException e){
            jobAccountLog.setMessage("调用获取AmazonADReportId错误:" + ExceptionUtil.transform(e));
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return null;
    }

    private DcBaseCompanyAccount getToken(JobAccountLog jobAccountLog) {
        DcBaseCompanyAccountMapper mapper = SessionUtil.getSession().getMapper(DcBaseCompanyAccountMapper.class);
        return  mapper.selectOneByExample(DcBaseCompanyAccountExample.newAndCreateCriteria().andCompanyIdEqualTo(jobAccountLog.getCompanyId()).andAccountIdEqualTo(jobAccountLog.getAccountId()).example());
    }

    private int getCount()  {
        int i = 0;
        try {
            DcBaseCompanyAccountMapper accountMapper = SessionUtil.getSession().getMapper(DcBaseCompanyAccountMapper.class);
            i = (int)accountMapper.countByExample(DcBaseCompanyAccountExample.newAndCreateCriteria().andPlatformIdEqualTo(platformId).example());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            SessionUtil.closeSession();
        }
        return i;
    }
}
