package org.jeecg.ctop.material.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.util.DateUtils;
import org.jeecg.ctop.material.constants.Check;
import org.jeecg.ctop.material.entity.vo.KuaiShouOperateGroupVo;
import org.jeecg.ctop.material.mapper.AdqAdvertiserReportMapper;
import org.jeecg.ctop.material.mapper.MaterialStareMapper;
import org.jeecg.ctop.material.service.IAdqAdvertiserReportService;
import org.jeecg.ctop.material.service.support.DashboardQuerySupport;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AdqAdvertiserReportServiceImpl implements IAdqAdvertiserReportService {

    @Resource
    private AdqAdvertiserReportMapper adqAdvertiserReportMapper;

    @Resource
    private DashboardQuerySupport dashboardQuerySupport;

    @Resource
    private MaterialStareMapper materialStareMapper;

    @Override
    public JSONObject getTotalCharge(Map<String, Object> paramsMap) {
        CompletableFuture<JSONObject> hourlyFuture = CompletableFuture.supplyAsync(
                () -> {
                    long start = System.currentTimeMillis();
                    try {
                        return adqAdvertiserReportMapper.getTotalChargeHourly(paramsMap);
                    } finally {
                        log.info("adq getTotalChargeHourly finished in {} ms", System.currentTimeMillis() - start);
                    }
                }
        );
        CompletableFuture<JSONObject> dailyFuture = CompletableFuture.supplyAsync(
                () -> {
                    long start = System.currentTimeMillis();
                    try {
                        return adqAdvertiserReportMapper.getTotalChargeDaily(paramsMap);
                    } finally {
                        log.info("adq getTotalChargeDaily finished in {} ms", System.currentTimeMillis() - start);
                    }
                }
        );

        JSONObject hourly = hourlyFuture.join();
        JSONObject daily = dailyFuture.join();
        JSONObject result = new JSONObject();
        result.put("todayCost", hourly.getBigDecimal("todayCost"));
        result.put("yesterdayCost", hourly.getBigDecimal("yesterdayCost"));
        result.put("thisWeekCost", daily.getBigDecimal("thisWeekDailyCost").add(hourly.getBigDecimal("thisWeekTodayCost")));
        result.put("lastWeekCost", daily.getBigDecimal("lastWeek1").add(hourly.getBigDecimal("lastWeek2")));
        return result;
    }

    @Override
    public JSONObject getTrendCharge(String type, Long projectId, String startTime, String endTime) {
        Integer startTimeInt = DateUtils.getDateInteger(startTime);
        Integer endTimeInt = DateUtils.getDateInteger(endTime);
        boolean currentDayOnly = startTime.equals(endTime) && startTime.equals(DateUtils.formatDate(new Date()));

        List<JSONObject> today = null;
        List<JSONObject> yesterday = null;
        if ("1".equals(type)) {
            today = adqAdvertiserReportMapper.getProjectNoByDays(startTimeInt, endTimeInt);
        } else if ("2".equals(type)) {
            today = adqAdvertiserReportMapper.getAdvertiserNoByDays(startTimeInt, endTimeInt);
        } else if ("3".equals(type)) {
            if (currentDayOnly) {
                today = adqAdvertiserReportMapper.getUnitNoByHours(startTimeInt);
            } else {
                today = adqAdvertiserReportMapper.getUnitNoByDays(startTimeInt, endTimeInt);
            }
        } else {
            if (currentDayOnly) {
                today = adqAdvertiserReportMapper.getHourCost(projectId, startTimeInt);
                yesterday = adqAdvertiserReportMapper.getHourCost(projectId, DateUtils.getDateInteger(DateUtils.getLastDay(startTime, 1)));
            } else {
                today = adqAdvertiserReportMapper.getDataCost(projectId, startTimeInt, endTimeInt);
                fillingDates(startTime, endTime, today);
                int days = DateUtils.dateDiff(startTime, endTime);
                String endTime2 = DateUtils.getLastDay(startTime, 1);
                String startTime2 = DateUtils.getLastDay(endTime2, days);
                yesterday = adqAdvertiserReportMapper.getDataCost(projectId, DateUtils.getDateInteger(startTime2), DateUtils.getDateInteger(endTime2));
                fillingDates(startTime2, endTime2, yesterday);
            }
        }
        JSONObject data = new JSONObject();
        data.put("today", today);
        data.put("yesterday", yesterday);
        return data;
    }

    private void fillingDates(String startTime, String endTime, List<JSONObject> today) {
        long dayNum = DateUtils.getDiscrepantDays(startTime, endTime) + 1;
        int size = today.size();
        if (dayNum - size != 0) {
            List<String> days = DateUtils.getDays(startTime, endTime);
            Set<String> existingDays = today.stream().map(item -> item.getString("time")).collect(Collectors.toSet());
            for (String day : days) {
                if (!existingDays.contains(day)) {
                    JSONObject obj = new JSONObject();
                    obj.put("time", day);
                    obj.put("cost", 0.00);
                    today.add(obj);
                }
            }
        }
    }

    @Override
    public Result<Object> getProjectReport(String category, Long projectId, String startTime, String endTime, String sortCode, String order, Integer pageSize, Integer pageNo, String userId, Boolean allData) throws Exception {
        if (Check.isNull(order)) {
            order = "DESC";
        }
        if (Check.isNull(sortCode)) {
            sortCode = "cost";
        }
        Integer startTimeInt = DateUtils.getDateInteger(startTime);
        Integer endTimeInt = DateUtils.getDateInteger(endTime);
        Integer nowHour = DateUtils.getNowHour();
        String today = DateUtils.formatDate(new Date());

        List<JSONObject> data;
        List<String> userIds = resolveTencentUserIds(userId);
        if (startTime.equals(endTime) && today.equals(startTime)) {
            if (!Boolean.TRUE.equals(allData)) {
                PageHelper.startPage(pageNo, pageSize, false);
            }
            data = adqAdvertiserReportMapper.getProjectToDayReport(category, projectId, startTimeInt, nowHour, sortCode, order, userIds);
        } else {
            int days = DateUtils.dateDiff(startTime, endTime) + 1;
            Integer lastStart = DateUtils.getDateInteger(DateUtils.getLastDay(startTime, days));
            Integer lastEnd = DateUtils.getDateInteger(DateUtils.getLastDay(endTime, days));
            if (!Boolean.TRUE.equals(allData)) {
                PageHelper.startPage(pageNo, pageSize, false);
            }
            data = adqAdvertiserReportMapper.getProjectMoreDaysReport(category, projectId, startTimeInt, endTimeInt, lastStart, lastEnd, sortCode, order, userIds);
        }
        PageInfo<JSONObject> page = new PageInfo<>(data);
        return Result.OK(page);
    }

    @Override
    public Result<Object> getOperateInfo(String startTime, String endTime) {
        try {
            Integer hour = DateUtils.getNowHour();
            String nowDateStr = DateUtils.formatDate(new Date());

            Long startDate = Long.valueOf(DateUtils.getDateInteger(startTime));
            Long entDate = Long.valueOf(DateUtils.getDateInteger(endTime));
            String lastDay = DateUtils.getLastDay(startTime, 1);
            int days = DateUtils.dateDiff(startTime, endTime);
            Long lastTimeEnd = Long.valueOf(DateUtils.getDateInteger(lastDay));
            Long lastTimeStart = Long.valueOf(DateUtils.getDateInteger(DateUtils.getLastDay(lastDay, days)));

            List<String> userIds = dashboardQuerySupport.getAllOperationUserIds("16");
            List<JSONObject> list;
            if (startTime.equals(endTime) && startTime.equals(nowDateStr)) {
                list = adqAdvertiserReportMapper.getAdqOperateInfoHourly(hour, startDate, entDate, lastTimeEnd, lastTimeStart, userIds);
            } else {
                list = adqAdvertiserReportMapper.getAdqOperateInfo(startDate, entDate, lastTimeEnd, lastTimeStart, userIds);
            }
            PageInfo<JSONObject> pageInfo = new PageInfo<>(list);
            return Result.successMsg("查询成功。", pageInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.errorMsg("查询失败了。");
    }

    @Override
    public Result<Object> getOperateGroupInfo(String startTime, String endTime) {
        try {
            Integer hour = DateUtils.getNowHour();
            String nowDateStr = DateUtils.formatDate(new Date());

            Long startDate = Long.valueOf(DateUtils.getDateInteger(startTime));
            Long entDate = Long.valueOf(DateUtils.getDateInteger(endTime));
            String lastDay = DateUtils.getLastDay(startTime, 1);
            int days = DateUtils.dateDiff(startTime, endTime);
            Long lastTimeEnd = Long.valueOf(DateUtils.getDateInteger(lastDay));
            Long lastTimeStart = Long.valueOf(DateUtils.getDateInteger(DateUtils.getLastDay(lastDay, days)));

            List<JSONObject> list = new ArrayList<>();
            List<KuaiShouOperateGroupVo> operationManagerList = dashboardQuerySupport.getTencentOperationManagers();
            List<String> managerIds = operationManagerList.stream().map(KuaiShouOperateGroupVo::getUserId).collect(Collectors.toList());
            Map<String, String> managerNameMap = operationManagerList.stream().collect(Collectors.toMap(KuaiShouOperateGroupVo::getUserId, KuaiShouOperateGroupVo::getRealname, (left, right) -> left));
            if (managerIds.isEmpty()) {
                return Result.successMsg("查询成功。", new PageInfo<>(list));
            }
            if (startTime.equals(endTime) && startTime.equals(nowDateStr)) {
                list = adqAdvertiserReportMapper.getAdqOperateGroupInfoHourlyBatch(managerIds, hour, startDate, entDate, lastTimeEnd, lastTimeStart);
            } else {
                list = adqAdvertiserReportMapper.getAdqOperateGroupInfoBatch(managerIds, startDate, entDate, lastTimeEnd, lastTimeStart);
            }
            for (JSONObject row : list) {
                row.put("realname", managerNameMap.get(row.getString("userId")));
                double totalCost = getDouble(row, "totalCost");
                double lastCost = getDouble(row, "lastCost");
                int operationNum = row.getInteger("operationNum") == null ? 0 : row.getInteger("operationNum");
                long accountNum = getLong(row, "accountNum");
                row.put("yearOnYearCost", lastCost == 0D ? "0%" : new BigDecimal((totalCost - lastCost) / lastCost * 100).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "%");
                row.put("averageDayCost", new BigDecimal(totalCost / (days + 1)).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue());
                row.put("averagePeopleCost", new BigDecimal(operationNum == 0 ? 0D : totalCost / operationNum).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue());
                row.put("averageAccountCost", new BigDecimal(accountNum == 0 ? 0D : totalCost / accountNum).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue());
            }
            PageInfo<JSONObject> pageInfo = new PageInfo<>(list);
            return Result.successMsg("查询成功。", pageInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.errorMsg("查询失败了。");
    }

    private Map<String, JSONObject> toMetricMap(List<JSONObject> rows) {
        if (rows == null || rows.isEmpty()) {
            return Collections.emptyMap();
        }
        return rows.stream()
                .filter(item -> item != null && item.getString("userId") != null)
                .collect(Collectors.toMap(item -> item.getString("userId"), item -> item, (left, right) -> left));
    }

    private void sortProjectRows(List<JSONObject> rows, String sortCode, String order) {
        if (rows == null || rows.isEmpty()) {
            return;
        }
        String actualSortCode = Check.isNull(sortCode) || "charge".equals(sortCode) ? "cost" : sortCode;
        Comparator<JSONObject> comparator = (left, right) -> compareSortValue(left.get(actualSortCode), right.get(actualSortCode));
        if ("ASC".equalsIgnoreCase(order)) {
            rows.sort(comparator);
        } else {
            rows.sort(comparator.reversed());
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private int compareSortValue(Object left, Object right) {
        if (left == right) {
            return 0;
        }
        if (left == null) {
            return -1;
        }
        if (right == null) {
            return 1;
        }
        if (left instanceof Number || right instanceof Number) {
            return BigDecimal.valueOf(Double.parseDouble(String.valueOf(left)))
                    .compareTo(BigDecimal.valueOf(Double.parseDouble(String.valueOf(right))));
        }
        if (left instanceof Comparable && right instanceof Comparable) {
            return ((Comparable) left).compareTo(right);
        }
        return String.valueOf(left).compareTo(String.valueOf(right));
    }

    private long getLong(JSONObject row, String key) {
        if (row == null) {
            return 0L;
        }
        Long value = row.getLong(key);
        return value == null ? 0L : value;
    }

    private double getDouble(JSONObject row, String key) {
        if (row == null) {
            return 0D;
        }
        Double value = row.getDouble(key);
        return value == null ? 0D : value;
    }

    private String getString(JSONObject row, String key) {
        return row == null ? null : row.getString(key);
    }

    private List<String> resolveTencentUserIds(String userId) {
        if (StringUtils.isBlank(userId)) {
            return dashboardQuerySupport.getAllOperationUserIds("16");
        }
        return new ArrayList<>(getAffiliateId(userId));
    }

    private Set<String> getAffiliateId(String userId) {
        String roleCode = materialStareMapper.getRoleCodeByUserId(userId);
        Set<String> result;
        Set<String> subordinate = materialStareMapper.getAffiliateId(userId, null);
        if (subordinate.isEmpty()) {
            if ("admin".equals(roleCode)) {
                return materialStareMapper.getCompanyAllUserId(null);
            }
            subordinate.add(userId);
            return subordinate;
        } else {
            result = materialStareMapper.getSaleDirctorItsTeamMember(userId);
            result.add(userId);
        }
        return result;
    }
}
