package com.reyun.report;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.*;
import com.reyun.model.ReportListResult;
import com.reyun.util.DateUtil;
import com.reyun.util.ViewColumnDependUtil;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.ion.Decimal;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * Created by nolan on 18/01/2017.
 * description:
 */
public abstract class AbstractNoDivideReportParse
        extends AbstractReportParse {
    private static final Logger logger = LoggerFactory.getLogger(AbstractNoDivideReportParse.class);
    private static final List<String> RP_CHANNEL = ImmutableList.of("channelinfo_bychannel", "h5_channel_detail_list", "channelinfo_order_bychannel");
    private static final List<String> RP_CAMPAIGN = ImmutableList.of("delay_click_bycampaign", "delay_install_bycampaign", "recycle_dashboard_campaign", "recycle_analysis_bycampaign");
    private static final List<String> RP_CAMPAIGN_GROUP = ImmutableList.of("");

    /**
     * 解析报表串
     *
     * @param reportResult
     * @param isSearchText
     * @param searchIds
     * @return
     */
    public abstract ReportListResult parse(ReportListResult reportResult, Long app, Long account, List<String> viewColumns, String reportName, boolean isSearchText, List<String> searchIds) throws JSONException;
    public abstract ReportListResult parse(ReportListResult reportResult, Long app, Long account, List<String> viewColumns, String reportName, boolean isSearchText, List<String> searchIds,Map<String, String> conditions);

    public abstract ReportListResult parse(ReportListResult reportResult, Long app, Long account, List<String> viewColumns, String reportName, boolean isSearchText, List<String> searchIds, Long cid, Map<String, String> conditions) throws JSONException;

    public abstract ReportListResult parse(ReportListResult reportResult, Long app, Long account, List<String> viewColumns, String reportName, boolean isSearchText, List<String> searchIds, Long cid) throws JSONException;

    protected boolean isReplaceLableKey(String name) {
        return RP_CHANNEL.contains(name) || RP_CAMPAIGN.contains(name);
    }

    protected boolean isRPCampaign(String name) {
        return RP_CAMPAIGN.contains(name);
    }

    protected boolean isRPChannel(String name) {
        return RP_CHANNEL.contains(name);
    }

    protected boolean isRPCampaignGroup(String name) {
        return RP_CAMPAIGN_GROUP.contains(name);
    }

    protected List<Map<String, Object>> calFirstTotalRow(ReportListResult reportResult, List<String> viewColumns) {
        List<Map<String, Object>> sourceResult = reportResult.getVal();
        Map<String, Object> totalRowMap = Maps.newHashMap();

        if (sourceResult == null || sourceResult.size() == 0) {
            for (String fieldcode : viewColumns) {
                Object tmp = null;
                if (isRateFields(fieldcode)) {
                    tmp = 0.0;
                } else {
                    tmp = 0l;
                }
                totalRowMap.put(fieldcode, tmp);
            }
            totalRowMap.put("isall0", false);
            totalRowMap.put("istotal", true);
            totalRowMap.put(firstTotalRowNeedReplaceId(), "汇总");
            totalRowMap.put("amt_perticket", 0);
            totalRowMap.put("amt_pay_perticket", 0);
            totalRowMap.put("rate_pay_order", 0);
            totalRowMap.put("amt_time", 0);
            return ImmutableList.of(totalRowMap);
        }

        Map<String, Integer> viewColumnsMap = viewColumns.stream().collect(Collectors.toMap(v -> v, v -> 0, (v1, v2) -> v1));
        List<String> viewColumnsFull = new ArrayList<>();

        for (String fieldcode : viewColumns) {
            Collection<String> linkfields = ViewColumnDependUtil.getRateDependFields(fieldcode);
            if (linkfields != null && linkfields.size() > 0) {
                String fieldNext = (String) new ArrayList(linkfields).get(1);
                if (!viewColumnsMap.containsKey(fieldNext)) {
                    viewColumnsFull.add(fieldNext);
                }
            }
        }

        viewColumnsFull.addAll(viewColumns);

        //分列 计算 留存指标等
        ListMultimap<String, Object> attributeMapList = ArrayListMultimap.create();

        for (int i = 0, n = sourceResult.size(); i < n; i++) {
            Map<String, Object> sourceItem = sourceResult.get(i);
            boolean isall0 = true;

            for (String fieldcode : viewColumnsFull) {
                Object v = sourceItem.get(fieldcode);
                boolean isRateFields = isRateFields(fieldcode);

                if (v == null) {
                    if (isRateFields) {
                        v = 0.0;
                    } else {
                        v = 0l;
                    }

                    sourceItem.put(fieldcode, v);
                    attributeMapList.put(fieldcode, v);

                } else {
                    Object linkfieldVal = null;
                    String[] linkfield = null;

                    Collection<String> linkfields = ViewColumnDependUtil.getRateDependFields(fieldcode);
                    if (linkfields != null && linkfields.size() > 0) {

                        linkfield = new String[linkfields.size()];
                        linkfield = linkfields.toArray(linkfield);

                        final Object f1 = sourceItem.get(linkfield[0]);

                        linkfieldVal = sourceItem.get(linkfield[1]);

                        if (linkfieldVal == null) {
                            linkfieldVal = "0";
                        }

                        Object rateValue;

                        boolean isDouble = f1.getClass().isAssignableFrom(Double.class) ? true : false;

                        if (fieldcode.startsWith("amt_ltv")) {
                            //非百分比计算
                            rateValue = divideTwoValue(f1, linkfieldVal, 100, isDouble, false);
                        } else {
                            rateValue = divideTwoValue(f1, linkfieldVal, 1, isDouble, true);
                        }


                        if (isall0) {
                            //isall0 有一个是 false 则不再判断
                            isall0 = (Double) rateValue > 0 ? false : true;
                        }
                        
                        sourceItem.put(fieldcode, rateValue);

                    } else if (sourceItem.get(fieldcode) != null && sourceItem.get(fieldcode).getClass().isAssignableFrom(Long.class) && fieldcode.toLowerCase().equals("duration")) {
                        sourceItem.put(fieldcode, DateUtil.formatCaseTime(Long.valueOf(sourceItem.get(fieldcode) == null ? "0" : sourceItem.get(fieldcode).toString())));
                    }

                    attributeMapList.put(fieldcode, v);

                    if (isall0) {
                        //isall0 有一个是 false 则不再判断
                        if (v.getClass().isAssignableFrom(Long.class) && (Long) v > 0) {
                            isall0 = false;
                        } else if (v.getClass().isAssignableFrom(Double.class) && (Double) v > 0) {
                            isall0 = false;
                        }
                    }


                }
            }

            sourceItem.put("isall0", isall0);

        }

        boolean isall0 = true;
        //计算汇总行

        for (String fieldcode : viewColumnsFull) {
            List<Object> listbyAttribute = attributeMapList.get(fieldcode);
            if (listbyAttribute == null || listbyAttribute.size() == 0) {
                continue;
            }

            Object calVal = null;
            Object[] obj = listbyAttribute.toArray();

            int line_number = 0;
            try {
                if (obj[0] instanceof Long) {
                    Long[] f2 = new Long[obj.length];
                    for (int i = 0; i < obj.length; i++) {
                        line_number = i;
                        String obValue = obj[i] == null ? "0" : obj[i].toString();
                        if (obValue.equals("0.0")) {
                            obValue = "0";
                        }

                        f2[i] = Long.valueOf(obValue);
                    }
                    calVal = plus(f2);
                } else if (obj[0] instanceof Double) {
                    Double[] f2 = new Double[obj.length];
                    for (int i = 0; i < obj.length; i++) {
                        line_number = i;
                        f2[i] = Double.valueOf(String.valueOf(obj[i]));
                    }
                    calVal = plus(f2);
                }
            } catch (ArrayStoreException e) {
                String valueAsString = "";
                try {
                    valueAsString = new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(obj);
                } catch (JsonProcessingException e1) {
                    logger.error("解析汇总数据错误", valueAsString, e1);
                }
                logger.error("汇总数据复制数据错误", valueAsString, e);
            } catch (Exception e) {
                logger.error("解析汇总数数据时发生错误, 第{}行, colum:{} ", line_number, fieldcode, e);
            }
            totalRowMap.put(fieldcode, calVal);

            if (!firstTotalRowNeedReplaceId().equals(fieldcode) && isall0) {
                if (calVal != null && Double.valueOf(String.valueOf(calVal)) > 0) {
                    isall0 = false;
                }
            }
        }

        //根据依赖字段计算比率值
        for (String fieldcode : viewColumns) {

            if (fieldcode.toLowerCase().equals("duration")) {
                totalRowMap.put(fieldcode, DateUtil.formatCaseTime(Long.valueOf(totalRowMap.get(fieldcode) == null ? "0" : totalRowMap.get(fieldcode).toString())));
            }

            Collection<String> linkfields = ViewColumnDependUtil.getRateDependFields(fieldcode);
            if (linkfields == null || linkfields.size() == 0) {
                continue;
            }

            String[] linkfield = new String[linkfields.size()];
            linkfield = linkfields.toArray(linkfield);

            final Object f1 = totalRowMap.get(linkfield[0]);
            if (f1 == null) {
                totalRowMap.put(fieldcode, 0);
                continue;
            }

            Object calVal = null;
            Object linkFieldValue2 = totalRowMap.get(linkfield[1]);
            if (linkFieldValue2 != null) {
                if (f1.getClass().isAssignableFrom(Long.class)) {

                    if (fieldcode.startsWith("amt_ltv")) {
                        calVal = divideAmt(((Long) f1) / 100, (Long) linkFieldValue2);
                    } else if (fieldcode.startsWith("rate_retentiond")) {
                        calVal = divide(((Long) f1), (Long) linkFieldValue2);
                    } else if (fieldcode.startsWith("avg_time")) {
                        calVal = divideAvgTime(((Long) f1), (Long) linkFieldValue2);
                    } else if (fieldcode.equals("amt_use_freq")) {
                        calVal = divideAmt(((Long) f1) / 100.0, Long.valueOf(String.valueOf(linkFieldValue2)) * 1.0);
                    } else {

                        if (linkFieldValue2 == null) {
                            linkFieldValue2 = "0";
                        }
                        calVal = divide((Long) f1, Long.parseLong(linkFieldValue2.toString()));
                    }
                } else if (f1.getClass().isAssignableFrom(Double.class)) {
                    if (fieldcode.startsWith("amt_ltv")) {
                        calVal = divideAmt(((Double) f1) / 100, Double.valueOf(String.valueOf(linkFieldValue2)));
                    } else if (fieldcode.startsWith("rate_retentiond")) {
                        calVal = divide(((Double) f1), Double.valueOf(String.valueOf(linkFieldValue2)));
                    } else if (fieldcode.startsWith("amt_arp") && fieldcode.endsWith("_new")) {
                        calVal = divideAmt(((Double) f1) / 100, Double.valueOf(String.valueOf(linkFieldValue2)));
                    } else if (fieldcode.startsWith("amt_arp")) {
                        calVal = divide(((Double) f1) / 100, Double.valueOf(String.valueOf(linkFieldValue2)));
                    } else if (fieldcode.equals("amt_pay_perticket") || fieldcode.equals("amt_perticket")) {
                        calVal = divide(((Double) f1) / 100, Double.valueOf(String.valueOf(linkFieldValue2)));
                    } else if (fieldcode.equals("amt_time")) {
                        calVal = divideAmt(((Double) f1) / 100, Double.valueOf(String.valueOf(linkFieldValue2)));
                    } else {
                        calVal = divide((Double) f1, Double.valueOf(String.valueOf(
                                getNewRateInstallData(fieldcode, totalRowMap, linkFieldValue2))));
                    }
                }
            } else {
                calVal = 0.0d;
            }
            totalRowMap.put(fieldcode, calVal);
        }
        totalRowMap.put("isall0", false);
        totalRowMap.put("istotal", true);

        totalRowMap.put(firstTotalRowNeedReplaceId(), "汇总");

        List<Map<String, Object>> newResult = Lists.newArrayList();
        newResult.add(totalRowMap);

        newResult.addAll(sourceResult);

        return newResult;
    }


    protected String firstTotalRowNeedReplaceId() {
        return "";
    }

    protected void setDefaultValue(ReportListResult reportListResult, List<String> searchIds) {
        if (searchIds != null && reportListResult.getKey() != null) {
            List<String> diffList = Lists.newArrayList(Sets.difference(Sets.newHashSet(searchIds), Sets.newHashSet(reportListResult.getKey())));
            reportListResult.getKey().addAll(diffList);
            for (String diffid : diffList) {
                Map<String, Object> apdItem = Maps.newHashMap();
                for (Object key : reportListResult.getColumnkey()) {
                    final String column = String.valueOf(key);
                    if (isRateFields(column)) {
                        apdItem.put(column, 0.0);
                    } else {
                        apdItem.put(column, 0l);
                    }
                    if (column.contains("rate_retentiond3") && !apdItem.containsKey("num_d3_install")) {
                        apdItem.put("num_d3_install", 0l);
                    }
                    if (column.contains("rate_retentiond7") && !apdItem.containsKey("num_d7_install")) {
                        apdItem.put("num_d7_install", 0l);
                    }
                    if (column.contains("rate_retentiond30") && !apdItem.containsKey("num_d30_install")) {
                        apdItem.put("num_d30_install", 0l);
                    }
                }


                apdItem.put(firstTotalRowNeedReplaceId(), diffid);
                apdItem.put("isall0", true);
                reportListResult.getVal().add(apdItem);
            }
        }
    }


    protected ReportListResult getMapTotalData(ReportListResult reportResult, String totalName, Map nameMap) {

        return getMapTotalData(reportResult, totalName, nameMap, null);
    }


    protected ReportListResult getMapTotalData(ReportListResult reportResult, String totalName, Map nameMap, Map<String, String> rateCuloms) {

        List<Object> columnkeys = reportResult.getColumnkey();

        Map<String, Object> totalMap = new HashMap<>();

        List<String> reteNames = new ArrayList<>();

        for (Object colum : columnkeys) {
            if (colum != null && !colum.toString().startsWith("rate_")) {
                totalMap.put(colum.toString(), 0L);
            } else {
                reteNames.add(colum.toString());
            }
        }

        List<Map<String, Object>> res = new LinkedList<>();

        List<Map<String, Object>> valList = reportResult.getVal();

        Set<String> keys = totalMap.keySet();
        String reportName = reportResult.getReportname().get(0);
        boolean isMttiReport = reportName.startsWith("black_top5_list_mtti_");

        for (Map<String, Object> data : valList) {

            for (String kname : keys) {

                if (!totalName.equals(kname)) {
                    totalMap.put(kname, Long.parseLong(totalMap.get(kname).toString()) + Long.parseLong(data.get(kname).toString()));

                    if (isMttiReport && !kname.equals("sum_install")) {

                        getRateTotal(data, "rate_" + kname, kname, "sum_install");

                        data.remove(kname);

                    }

                } else {
                    if (nameMap != null) {
                        Object nameshow = nameMap.get(data.get(totalName).toString());
                        if (nameshow != null) {
                            data.put(totalName, nameshow);
                        }
                    }
                }


            }

            res.add(data);
        }

        totalMap.put(totalName, "汇总");

        if (rateCuloms != null) {

            for (String name : reteNames) {

                if (rateCuloms.get(name) != null) {
                    this.getRateTotal(totalMap, name, rateCuloms.get(name).split(","));
                }
            }

        } else {
            this.getRateTotal(totalMap, "rate_click", "num_black", "num_click");
            this.getRateTotal(totalMap, "rate_install", "num_total_install_black", "num_install");
        }

        res.add(0, totalMap);
        reportResult.setVal(res);
        return reportResult;

    }

    private void getRateTotal(Map<String, Object> totalMap, String rateName, String... keys) {

        long valueA = Long.parseLong(totalMap.get(keys[0]).toString());
        long valueB = Long.parseLong(totalMap.get(keys[1]).toString());

        if (valueB != 0 && valueA != 0) {
            double v = valueA * 100.0 / valueB;
            DecimalFormat df = new DecimalFormat("#0.00");
            totalMap.put(rateName, df.format(v));
        } else {
            totalMap.put(rateName, "0.00");
        }

    }


    protected List<Map<String, Object>> calFirstTotalRowSimple(ReportListResult reportResult, List<String> viewColumns, List<String> specilColums, String reportName, String totalKey) {

        List<Map<String, Object>> mapValue = reportResult.getVal();

        if (mapValue == null) {
            return mapValue;
        }

        List<String> formatKeys = Arrays.asList("cpc_num", "cpm_num");

        Map total_all_ = new HashMap();

        int[] isall0 = new int[mapValue.size()];

        List<String> totalColums = specilColums == null ? viewColumns : specilColums;

        for (String colm : totalColums) {

            if (totalKey.equals(colm)) {
                total_all_.put(colm, "汇总");
                continue;
            }

            Double total_double = 0.0;
            Long total_long = 0L;
            boolean isLongdata = true;

            for (int i = 0, j = mapValue.size(); i < j; i++) {

                Map<String, Object> dataItem = mapValue.get(i);
                dataItem.put("isall0", true);

                if (i > 0 && isall0[i - 1] == 1) {
                    mapValue.get(i - 1).put("isall0", false);
                }

                Object value = dataItem.get(colm);
                if (value == null) {
                    continue;
                }
                if (value instanceof Long) {
                    total_long += (Long) value;

                    if (isall0[i] == 0 && (Long) value > 0) {
                        isall0[i] = 1;
                    }

                } else if (value instanceof Double) {
                    isLongdata = false;
                    total_double += (Double) value;

                    if (isall0[i] == 0 && (Double) value > 0) {
                        isall0[i] = 1;
                    }
//
//                    //格式化小数
                }

                if (colm.equals(totalColums.get(0))) {
                    //格式化小数
                    for (String keyc : formatKeys) {
                        Object dataCpm = dataItem.get(keyc);
                        if (dataCpm != null && dataCpm instanceof Double) {
                            dataItem.put(keyc, new BigDecimal((double) dataItem.get(keyc)).setScale(2, Decimal.ROUND_HALF_UP).doubleValue());
                        }
                    }
                }

            }

            if (isall0[isall0.length - 1] == 1) {
                mapValue.get(isall0.length - 1).put("isall0", false);
            }


            if (isLongdata) {
                total_all_.put(colm, total_long);
            } else {
                total_all_.put(colm, new BigDecimal(total_double).setScale(2, Decimal.ROUND_HALF_UP).doubleValue());
            }

        }

        total_all_.put("isall0", false);

        if (specilColums != null && specilColums.size() > 0) {
            for (String viewColumn : viewColumns) {
                if (!specilColums.contains(viewColumn)) {
                    total_all_.put(viewColumn, "-");
                }
            }
        }

        mapValue.add(0, total_all_);
        return mapValue;
    }


    protected void createColumByReplace(ReportListResult reportResult) {
        List<Object> keys = reportResult.getColumnkey();
        if (firstTotalRowNeedReplaceId().equals(keys.get(0)) || "".equals(firstTotalRowNeedReplaceId())) {
            return;
        }
        keys.remove(0);
        keys.add(0, firstTotalRowNeedReplaceId());

    }
}