package com.retdata.yaoyibi.service.impl;

import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.retdata.yaoyibi.domain.*;
import com.retdata.yaoyibi.mapper.CsoSysDeptMapper;
import com.retdata.yaoyibi.mapper.WmScorePackageMapper;
import com.retdata.yaoyibi.service.CsoSysDeptService;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.enums.DataSourceType;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import java.text.DecimalFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Transactional(readOnly = true)
@DataSource(DataSourceType.CSO)
public class CsoSysDeptServiceImpl extends ServiceImpl<CsoSysDeptMapper, CsoSysDept> implements CsoSysDeptService {

    private static DecimalFormat MONEY_FORMAT = new DecimalFormat("#,###");

    private static DecimalFormat PERCENT_FORMAT = new DecimalFormat("#.##%");

    private final WmScorePackageMapper wmScorePackageMapper;

    public CsoSysDeptServiceImpl(WmScorePackageMapper wmScorePackageMapper) {
        this.wmScorePackageMapper = wmScorePackageMapper;
    }

    @Override
    public Page<CsoSysDeptSearchDto> search(CsoSysDeptSearchParams searchParams, Pageable pageable) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<CsoSysDeptSearchDto> page =
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageable.getPageNumber() + 1,
                        pageable.getPageSize());
        page.addOrder(OrderItem.desc("d1.create_time"));
        page = getBaseMapper().selectPage(page, searchParams);
        if (page.getRecords().isEmpty()) {
            return new PageImpl<>(page.getRecords(), pageable, page.getTotal());
        }
        Map<Integer, String> signProductMap = getDeptSignProducts();
        calculateNrr(signProductMap, page.getRecords());
        if (searchParams.isFetchChildrenCount()) {
            Map<Integer, CsoSysDeptSearchDto> map = page.getRecords().stream()
                    .collect(Collectors.toMap(CsoSysDeptSearchDto::getDeptId, Function.identity()));
            List<CsoSysDeptChildrenDto> children = getBaseMapper().selectChildrenCount(map.keySet());
            children.forEach(child -> map.get(child.getDeptId()).setChildrenCount(child.getChildrenCount()));
        }
        return new PageImpl<>(page.getRecords(), pageable, page.getTotal());
    }

    private Map<Integer, String> getDeptSignProducts() {
        return getBaseMapper().selectAllSignProducts().stream()
                .collect(Collectors.toMap(SysDeptSignProduct::getDeptId, SysDeptSignProduct::getSignProduct));
    }

    private void calculateNrr(Map<Integer, String> signProductMap, Collection<CsoSysDeptSearchDto> records) {
        Map<Integer, CsoSysDeptSearchDto> cso1Depts = new HashMap<>();
        Map<Integer, CsoSysDeptSearchDto> cso2Depts = new HashMap<>();
        for (CsoSysDeptSearchDto dto : records) {
            String signProduct = signProductMap.get(dto.getDeptId());
            if (SysDeptSignProduct.CSO1.equals(signProduct)) {
                cso1Depts.put(dto.getDeptId(), dto);
            } else if (SysDeptSignProduct.CSO2.equals(signProduct)) {
                cso2Depts.put(dto.getDeptId(), dto);
            }
            dto.setSignProduct(signProduct);
        }
        int currentYear = LocalDate.now().getYear();
        int lastYear = currentYear - 1;
        if (!cso1Depts.isEmpty()) {
            List<DeptGmv> gmvList = wmScorePackageMapper.selectCso1GmvByDeptIds(cso1Depts.keySet(), lastYear, currentYear);
            for (DeptGmv gmv : gmvList) {
                CsoSysDeptSearchDto dto = cso1Depts.get(gmv.getRootDeptId());
                if (gmv.getLastYearScore() > 0) {
                    dto.setLastYearGmv(gmv.getLastYearScore() / gmv.getLastYearMonthCount() * 12);
                }
                if (gmv.getCurrentYearScore() > 0) {
                    dto.setCurrentYearGmv(gmv.getCurrentYearScore() / gmv.getCurrentYearMonthCount() * 12);
                }
                if (dto.getLastYearGmv() > 0) {
                    dto.setNrr(dto.getCurrentYearGmv() * 1.0 / dto.getLastYearGmv());
                }
            }
        }
        if (!cso2Depts.isEmpty()) {
            List<DeptGmv> gmvList = wmScorePackageMapper.selectCso2GmvByDeptIds(cso2Depts.keySet(), lastYear, currentYear);
            for (DeptGmv gmv : gmvList) {
                CsoSysDeptSearchDto dto = cso2Depts.get(gmv.getRootDeptId());
                if (gmv.getLastYearScore() > 0) {
                    dto.setLastYearGmv(gmv.getLastYearScore() / gmv.getLastYearMonthCount() * 12);
                }
                if (gmv.getCurrentYearScore() > 0) {
                    dto.setCurrentYearGmv(gmv.getCurrentYearScore() / gmv.getCurrentYearMonthCount() * 12);
                }
                if (dto.getLastYearGmv() > 0) {
                    dto.setNrr(dto.getCurrentYearGmv() * 1.0 / dto.getLastYearGmv());
                }
            }
        }
    }

    @Override
    public XSSFWorkbook export(CsoSysDeptSearchParams searchParams) {
        Map<Integer, CsoSysDeptSearchDto> deptCache = new HashMap<>();
        Map<Integer, CsoSysDeptSearchDto> toFetchDeptMap = new HashMap<>();
        List<CsoSysDeptSearchDto> list = getBaseMapper().selectAll(searchParams);
        list.forEach(dto -> deptCache.put(dto.getDeptId(), dto));
        List<CsoSysDeptParentDto> parents = getBaseMapper().selectParents();
        MultiValueMap<Integer, Integer> parentMap = new LinkedMultiValueMap<>();
        for (CsoSysDeptParentDto parent : parents) {
            parentMap.add(parent.getDeptId(), parent.getParentId());
        }
        List<CsoSysDeptExportRecord> records = new ArrayList<>(list.size());
        for (CsoSysDeptSearchDto dto : list) {
            CsoSysDeptExportRecord record = new CsoSysDeptExportRecord();
            List<Integer> parentIds = parentMap.get(dto.getDeptId());
            if (parentIds == null) {
                record.setRootDept(dto);
                records.add(record);
                continue;
            }
            for (Integer parentId : parentIds) {
                CsoSysDeptSearchDto parent = deptCache.get(parentId);
                if (parent == null) {
                    parent = new CsoSysDeptSearchDto();
                    parent.setDeptId(parentId);
                    toFetchDeptMap.put(parentId, parent);
                    deptCache.put(parentId, parent);
                }
                List<Integer> grandParentIds = parentMap.get(parentId);
                if (grandParentIds == null) {
                    record.setRootDept(parent);
                    record.setFirstDept(dto);
                    records.add(record);
                    continue;
                }
                for (Integer grandParentId : grandParentIds) {
                    CsoSysDeptSearchDto grandParent = deptCache.get(grandParentId);
                    if (grandParent == null) {
                        grandParent = new CsoSysDeptSearchDto();
                        grandParent.setDeptId(grandParentId);
                        toFetchDeptMap.put(grandParentId, grandParent);
                        deptCache.put(grandParentId, grandParent);
                    }
                    record.setRootDept(grandParent);
                    record.setFirstDept(parent);
                    record.setSecondDept(dto);
                    records.add(record);
                }
            }
        }
        if (!toFetchDeptMap.isEmpty()) {
            List<CsoSysDeptSearchDto> toFetchList = getBaseMapper().selectByIds(toFetchDeptMap.keySet());
            for (CsoSysDeptSearchDto dto : toFetchList) {
                CsoSysDeptSearchDto placeholder = toFetchDeptMap.get(dto.getDeptId());
                placeholder.setName(dto.getName());
                placeholder.setLevel(dto.getLevel());
                placeholder.setSubjectType(dto.getSubjectType());
                placeholder.setTaxCode(dto.getTaxCode());
                placeholder.setDelFlag(dto.getDelFlag());
                placeholder.setIsChannel(dto.getIsChannel());
                placeholder.setSubjectLocationAndLimitAmount(dto.getSubjectLocationAndLimitAmount());
                placeholder.setCertifiedUserCount(dto.getCertifiedUserCount());
                placeholder.setPackageUserCount(dto.getPackageUserCount());
            }
        }
        // 过滤掉非叶子节点
        records = filterNonLeafRecords(records);
        Set<CsoSysDeptSearchDto> exportedDeptSet = new HashSet<>();
        for (CsoSysDeptExportRecord record : records) {
            if (record.getRootDept() != null) {
                exportedDeptSet.add(record.getRootDept());
            }
            if (record.getFirstDept() != null) {
                exportedDeptSet.add(record.getFirstDept());
            }
            if (record.getSecondDept() != null) {
                exportedDeptSet.add(record.getSecondDept());
            }
        }
        Map<Integer, String> signProductMap = getDeptSignProducts();
        calculateNrr(signProductMap, exportedDeptSet);
        // 创建xlsx
        XSSFWorkbook workbook = new XSSFWorkbook();
        XSSFSheet sheet = workbook.createSheet("企业数据");
        int rowNo = 0;
        int columnNo = 0;
        // 创建表头
        XSSFRow row = sheet.createRow(rowNo++);
        String[] headers = {"药企名称", "是否渠道", "结算渠道及企业限额", "认证人数", "服务人数", "签约类型", "上年月均年化", "当年月均年化", "NRR(%)", "状态",
                "一级代理商名称", "是否渠道", "结算渠道及企业限额", "认证人数", "服务人数", "签约类型", "上年月均年化", "当年月均年化", "NRR(%)", "状态",
                "二级代理商名称", "是否渠道", "结算渠道及企业限额", "认证人数", "服务人数", "签约类型", "上年月均年化", "当年月均年化", "NRR(%)", "状态"};
        for (String header : headers) {
            row.createCell(columnNo++).setCellValue(header);
        }
        for (CsoSysDeptExportRecord record : records) {
            row = sheet.createRow(rowNo++);
            columnNo = 0;
            if (record.getRootDept() != null) {
                row.createCell(columnNo++).setCellValue(record.getRootDept().getName());
                row.createCell(columnNo++).setCellValue(Boolean.TRUE.equals(record.getRootDept().getIsChannel()) ? "是" : "否");
                row.createCell(columnNo++).setCellValue(formatSubjectLocationAndLimitAmount(
                        record.getRootDept().getSubjectLocationAndLimitAmount()));
                row.createCell(columnNo++).setCellValue(record.getRootDept().getCertifiedUserCount() == null ?
                        "" : record.getRootDept().getCertifiedUserCount().toString());
                row.createCell(columnNo++).setCellValue(record.getRootDept().getPackageUserCount() == null ?
                        "" : record.getRootDept().getPackageUserCount().toString());
                row.createCell(columnNo++).setCellValue(signProductMap.get(record.getRootDept().getDeptId()));
                row.createCell(columnNo++).setCellValue(record.getRootDept().getLastYearGmv());
                row.createCell(columnNo++).setCellValue(record.getRootDept().getCurrentYearGmv());
                row.createCell(columnNo++).setCellValue(PERCENT_FORMAT.format(record.getRootDept().getNrr()));
                row.createCell(columnNo++).setCellValue("0".equals(record.getRootDept().getDelFlag()) ? "启用" : "停用");
            } else {
                columnNo += 10;
            }
            if (record.getFirstDept() != null) {
                row.createCell(columnNo++).setCellValue(record.getFirstDept().getName());
                row.createCell(columnNo++).setCellValue(Boolean.TRUE.equals(record.getFirstDept().getIsChannel()) ? "是" : "否");
                row.createCell(columnNo++).setCellValue(formatSubjectLocationAndLimitAmount(
                        record.getFirstDept().getSubjectLocationAndLimitAmount()));
                row.createCell(columnNo++).setCellValue(record.getFirstDept().getCertifiedUserCount() == null ?
                        "" : record.getFirstDept().getCertifiedUserCount().toString());
                row.createCell(columnNo++).setCellValue(record.getFirstDept().getPackageUserCount() == null ?
                        "" : record.getFirstDept().getPackageUserCount().toString());
                row.createCell(columnNo++).setCellValue(signProductMap.get(record.getFirstDept().getDeptId()));
                row.createCell(columnNo++).setCellValue(record.getFirstDept().getLastYearGmv());
                row.createCell(columnNo++).setCellValue(record.getFirstDept().getCurrentYearGmv());
                row.createCell(columnNo++).setCellValue(PERCENT_FORMAT.format(record.getFirstDept().getNrr()));
                row.createCell(columnNo++).setCellValue("0".equals(record.getFirstDept().getDelFlag()) ? "启用" : "停用");
            } else {
                columnNo += 10;
            }
            if (record.getSecondDept() != null) {
                row.createCell(columnNo++).setCellValue(record.getSecondDept().getName());
                row.createCell(columnNo++).setCellValue(Boolean.TRUE.equals(record.getSecondDept().getIsChannel()) ? "是" : "否");
                row.createCell(columnNo++).setCellValue(formatSubjectLocationAndLimitAmount(
                        record.getSecondDept().getSubjectLocationAndLimitAmount()));
                row.createCell(columnNo++).setCellValue(record.getSecondDept().getCertifiedUserCount() == null ?
                        "" : record.getSecondDept().getCertifiedUserCount().toString());
                row.createCell(columnNo++).setCellValue(record.getSecondDept().getPackageUserCount() == null ?
                        "" : record.getSecondDept().getPackageUserCount().toString());
                row.createCell(columnNo++).setCellValue(signProductMap.get(record.getSecondDept().getDeptId()));
                row.createCell(columnNo++).setCellValue(record.getSecondDept().getLastYearGmv());
                row.createCell(columnNo++).setCellValue(record.getSecondDept().getCurrentYearGmv());
                row.createCell(columnNo++).setCellValue(PERCENT_FORMAT.format(record.getSecondDept().getNrr()));
                row.createCell(columnNo++).setCellValue("0".equals(record.getSecondDept().getDelFlag()) ? "启用" : "停用");
            }
        }
        return workbook;
    }

    private String formatSubjectLocationAndLimitAmount(String subjectLocationAndLimitAmount) {
        if (!StringUtils.hasText(subjectLocationAndLimitAmount)) {
            return "";
        } else {
            StringBuilder sb = new StringBuilder();
            String[] items = subjectLocationAndLimitAmount.split(",");
            for (String item : items) {
                String[] arr = item.split(":");
                if (sb.length() > 0) {
                    sb.append(',');
                }
                sb.append(arr[0]);
                sb.append(':');
                if (arr.length > 1 && StringUtils.hasText(arr[1])) {
                    sb.append(MONEY_FORMAT.format(Double.parseDouble(arr[1])));
                }
            }
            return sb.toString();
        }
    }

    private List<CsoSysDeptExportRecord> filterNonLeafRecords(Collection<CsoSysDeptExportRecord> records) {
        Set<List<Integer>> nonLeafPaths = new HashSet<>(records.size());
        List<CsoSysDeptExportRecord> result = new ArrayList<>(records.size());
        records.stream().sorted(Comparator.comparing(CsoSysDeptExportRecord::getDepth).reversed()).forEach(record -> {
            List<Integer> ids = record.getFullPath();
            if (nonLeafPaths.contains(ids)) {
                return;
            }
            for (int i = 0; i < ids.size() - 1; i++) {
                nonLeafPaths.add(ids.subList(0, i + 1));
            }
            result.add(record);
        });
        Collections.reverse(result);
        return result;
    }

    @Override
    public List<CsoSysDeptSummary> listAllDeptSummary() {
        return getBaseMapper().selectAllSummaries();
    }
}
