package com.kcim.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kcim.common.constants.NumberConstant;
import com.kcim.common.constants.SQLParameter;
import com.kcim.common.enums.CustomSqlTypeEnum;
import com.kcim.common.exception.CostException;
import com.kcim.common.util.BeanUtil;
import com.kcim.common.util.JacksonUtil;
import com.kcim.common.util.PageUtils;
import com.kcim.common.util.RedisLock;
import com.kcim.dao.mapper.CostIncomeGroupMapper;
import com.kcim.dao.mapper.IncomeCollectionMapper;
import com.kcim.dao.model.AfterIncomeGroup;
import com.kcim.dao.model.CostIncomeGroup;
import com.kcim.dao.model.CostIncomeGroupSet;
import com.kcim.dao.model.IncomeCollection;
import com.kcim.dao.model.dto.CollectDTO;
import com.kcim.service.CenterService;
import com.kcim.service.CostDataService;
import com.kcim.vo.*;
import com.kcim.service.CostIncomeGroupSetService;
import com.kcim.service.IncomeCollectionService;
import com.kcim.common.constants.Constant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.kcim.common.constants.ParameterConstant.INCOME_COLLECTION_TYPE;


@Slf4j
@Service("incomeCollectionService")
public class IncomeCollectionServiceImpl
        extends ServiceImpl<IncomeCollectionMapper, IncomeCollection>
        implements IncomeCollectionService {

    private final RedisLock redisLock;

    private final CostIncomeGroupMapper incomeGroupMapper;

    private final CostIncomeGroupSetService incomeGroupSetService;

    private final CenterService centerService;

    private final CostDataService costDataService;

    public IncomeCollectionServiceImpl(RedisLock redisLock, CostIncomeGroupMapper incomeGroupMapper, CostIncomeGroupSetService incomeGroupSetService, CenterService centerService, CostDataService costDataService) {
        this.redisLock = redisLock;
        this.incomeGroupMapper = incomeGroupMapper;
        this.incomeGroupSetService = incomeGroupSetService;
        this.centerService = centerService;
        this.costDataService = costDataService;
    }

    /**
     * 获取收入归集分页列表
     *
     * @param current  当前页
     * @param pageSize 页码数据大小
     * @param date     日期 yyyyMM
     * @param hospId   医院id
     * @return {@link PageUtils} 分页对象
     */
    @Override
    public PageUtils getCollections(Integer current, Integer pageSize, String date, Long hospId) {
        current = current - 1;
        Integer startIndex = current * pageSize;
        List<CollectionVO> list = incomeGroupMapper.getCollections(startIndex, pageSize, date, hospId);
        int count = incomeGroupMapper.getCollectionCount(date, hospId);

        // 设置是否归集字段
        list.forEach(i -> {
            i.setIsCollection(true);
            IncomeCollection one = this.getOne(
                    new LambdaQueryWrapper<IncomeCollection>().select(IncomeCollection::getId)
                            .eq(IncomeCollection::getYear, i.getYear())
                            .eq(IncomeCollection::getMonth, i.getMonth())
                            .eq(IncomeCollection::getHospId, hospId)
                            .last(Constant.LIMIT)
            );
            if (Objects.isNull(one)) {
                i.setIsCollection(false);
            }
        });


        return new PageUtils(list, count, pageSize, current + 1);
    }

    /**
     * 按年月归集数据
     *
     * @param year   年 数字类型
     * @param month  月 数字
     * @param hospId 医院id
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void collect(Integer year, Integer month, Long hospId) {
        CommonParameterVo parameter = centerService.getParameter(INCOME_COLLECTION_TYPE);
        String incomeCollectType = NumberConstant.ONE_S;
        if (Objects.nonNull(parameter)){
            incomeCollectType = parameter.getValue();
        }
        if(incomeCollectType.equals(NumberConstant.ONE_S)){
            incomeCollect(year, month, hospId);
        }else {
            String computeDate = month < 10 ? year + "-0" + month : year + "-" + month;
            Map<String,String> sqlParameter = new HashMap<>();
            sqlParameter.put(SQLParameter.COMPUTE_DATE_CODE,computeDate);
            costDataService.autoExecuteSql(CustomSqlTypeEnum.INCOME_COLLECTION.getCode(),sqlParameter);
        }

    }

    private void incomeCollect(Integer year, Integer month, Long hospId) {
        // todo  可能几十万次数据 需要分段异步多线程处理，
        // 1个小时
        String key = "collect" + hospId;
        String timestamp = DateUtil.offsetMillisecond(DateUtil.date(), 60 * 60).getTime() + "";
        boolean lock = redisLock.lock(key, timestamp);
        try {
            if (lock) {
                List<CostIncomeGroup> costIncomeGroups = incomeGroupMapper.selectList(
                        new LambdaQueryWrapper<CostIncomeGroup>()
                                .eq(CostIncomeGroup::getDateYear, year)
                                .eq(CostIncomeGroup::getDateMonth, month)
                                .eq(CostIncomeGroup::getHospId, hospId)
                );

                if (costIncomeGroups.isEmpty()) {
                    String format = StrUtil.format("{}年{}月数据不存在，请先导入数据", year, month);
                    throw new CostException(format);
                }

                // 删掉已归集的数据
                this.remove(
                        new LambdaQueryWrapper<IncomeCollection>().eq(IncomeCollection::getYear, year).eq(IncomeCollection::getMonth, month).eq(IncomeCollection::getHospId, hospId)
                );
                // 根据会计科目和成本项目归纳所有数据
                Map<String, List<CostIncomeGroup>> collectMap = costIncomeGroups.stream()
                        .collect(Collectors.groupingBy(i -> i.getOpenDepartmentCode() + "cost" + i.getStartDepartmentCode() + "cost" + i.getProductCode()));

                // 遍历map 组装成List保存
                List<IncomeCollection> list = new LinkedList<>();
                Set<Map.Entry<String, List<CostIncomeGroup>>> entries = collectMap.entrySet();
                entries.stream().map(Map.Entry::getValue).flatMap(Collection::stream).forEach(costIncomeGroup -> {
                    //TODO 统计总金额
                    IncomeCollection incomeCollection = new IncomeCollection();
                    incomeCollection.setYear(year);
                    incomeCollection.setMonth(month);
                    incomeCollection.setAccountingCode(costIncomeGroup.getAccountCode());
                    incomeCollection.setAccountingName(costIncomeGroup.getAccountName());
                    incomeCollection.setProductName(costIncomeGroup.getProductName());
                    incomeCollection.setProductCode(costIncomeGroup.getProductCode());
                    incomeCollection.setHospId(hospId);
                    // todo 来源id暂时不确定
                    incomeCollection.setFileId(costIncomeGroup.getFileId());
                    incomeCollection.setCreateTime(System.currentTimeMillis());
                    String afterIncomeGroupStr = costIncomeGroup.getAfterIncomeGroup();
                    AfterIncomeGroup afterIncomegroup = JacksonUtil.str2Obj(afterIncomeGroupStr, AfterIncomeGroup.class);
                    if (Objects.isNull(afterIncomegroup)) {
                        throw new CostException("未能正确归集对应的....");
                    }
                    // 转换一下其他的
                    if (afterIncomegroup.getOpenDepartmentStatus() == 1 && afterIncomegroup.getStartDepartmentStatus() == 1) {
                        // 排除会计科目
                        List<CostIncomeGroupSet> sets = incomeGroupSetService.list(
                                new LambdaQueryWrapper<CostIncomeGroupSet>()
                                        .eq(CostIncomeGroupSet::getOpenDepartmentStatus, 1).eq(CostIncomeGroupSet::getStartDepartmentStatus, 1).eq(CostIncomeGroupSet::getHospId, hospId)
                        );
                        if (!sets.isEmpty()) {
                            for (CostIncomeGroupSet set : sets) {
                                String accountCode = set.getAccountCode();
//                                if (StrUtil.isBlank(accountCode)) {
//                                    continue;
//                                }
                                if(StrUtil.isNotBlank(accountCode)){
                                    String[] split = accountCode.split(StrUtil.COMMA);
                                    boolean contains = CollUtil.newArrayList(split).contains(costIncomeGroup.getAccountCode());
                                    if (contains) {
                                        // 钱全给执行科室
                                        incomeCollection.setAmount(new BigDecimal("0.0000"));
                                        incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode());
                                        incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName());
                                        incomeCollection.setResponsibilityCode(afterIncomegroup.getOpenResponsibilityCode());
                                        incomeCollection.setResponsibilityName(afterIncomegroup.getOpenResponsibilityName());

                                        list.add(incomeCollection);
                                        // 执行科室数据

                                        IncomeCollection startCollect = BeanUtil.convertObj(incomeCollection, IncomeCollection.class);
                                        startCollect.setAmount(costIncomeGroup.getAmount());
                                        startCollect.setDepartmentCode(afterIncomegroup.getStartDepartmentCode());
                                        startCollect.setDepartmentName(afterIncomegroup.getStartDepartmentName());
                                        startCollect.setResponsibilityCode(afterIncomegroup.getStartResponsibilityCode());
                                        startCollect.setResponsibilityName(afterIncomegroup.getStartResponsibilityName());
                                        log.info("start:{}", startCollect);
                                        log.info("open:{}", incomeCollection);
                                        list.add(startCollect);
                                    } else {
                                        percentCollection(list, incomeCollection, afterIncomegroup);
                                    }
                                }else {
                                    percentCollection(list, incomeCollection, afterIncomegroup);
                                }

                            }
                        }
                        return;
                    }
                    percentCollection(list, incomeCollection, afterIncomegroup);

                });


                // TODO: 2021/8/10 几十万条数据如何处理 待测试处理

                Map<String, List<IncomeCollection>> collect = list.stream().collect(Collectors.groupingBy(i -> i.getDepartmentCode() + "cost" + i.getResponsibilityCode() + "cost" + i.getAccountingCode() + "cost" + i.getProductCode()));
                List<IncomeCollection> realList = new LinkedList<>();
                Set<String> strings = collect.keySet();
                for (String str : strings) {
                    List<IncomeCollection> incomeCollections = collect.get(str);
                    if (CollUtil.isNotEmpty(incomeCollections)) {
                        BigDecimal reduce = incomeCollections.stream().map(IncomeCollection::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                        IncomeCollection incomeCollection = incomeCollections.get(0);
                        incomeCollection.setAmount(reduce);
                        realList.add(incomeCollection);
                    }
                }

                this.saveBatch(realList);
            } else {
                throw new CostException("已有人在操作，请稍后重试");
            }
        } finally {
            redisLock.release(key, timestamp);
        }
    }

    private void percentCollection(List<IncomeCollection> list, IncomeCollection incomeCollection, AfterIncomeGroup afterIncomegroup) {
        if (afterIncomegroup.getDirectStatus() == 2) {
            afterIncomegroup.setDirectStatus(0);
        }
        incomeCollection.setIsDirectIncome(afterIncomegroup.getDirectStatus());
        String responsibilityCode = afterIncomegroup.getResponsibilityCode();
        if (StrUtil.isNotBlank(responsibilityCode)) {
            incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName() + "|" + afterIncomegroup.getStartDepartmentName());
            incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode() + "|" + afterIncomegroup.getStartDepartmentCode());
            incomeCollection.setResponsibilityCode(responsibilityCode);
            incomeCollection.setResponsibilityName(afterIncomegroup.getResponsibilityName());
            incomeCollection.setAmount(afterIncomegroup.getOtherResponsibilityDecimal());
            list.add(incomeCollection);
        } else {
            // 开单中心的数据
            incomeCollection.setAmount(afterIncomegroup.getOpenDepartmentDecimal());
            incomeCollection.setDepartmentCode(afterIncomegroup.getOpenDepartmentCode());
            incomeCollection.setDepartmentName(afterIncomegroup.getOpenDepartmentName());
            incomeCollection.setResponsibilityCode(afterIncomegroup.getOpenResponsibilityCode());
            incomeCollection.setResponsibilityName(afterIncomegroup.getOpenResponsibilityName());

            list.add(incomeCollection);
            // 执行科室数据

            IncomeCollection startCollect = BeanUtil.convertObj(incomeCollection, IncomeCollection.class);
            startCollect.setAmount(afterIncomegroup.getStartDepartmentDecimal());
            startCollect.setDepartmentCode(afterIncomegroup.getStartDepartmentCode());
            startCollect.setDepartmentName(afterIncomegroup.getStartDepartmentName());
            startCollect.setResponsibilityCode(afterIncomegroup.getStartResponsibilityCode());
            startCollect.setResponsibilityName(afterIncomegroup.getStartResponsibilityName());
            log.info("start:{}", startCollect);
            log.info("open:{}", incomeCollection);
            list.add(startCollect);
        }
    }

    /**
     * 按年月撤销归集
     *
     * @param year   年 数字类型
     * @param month  月 数字
     * @param hospId 医院id
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void cancelCollect(Integer year, Integer month, Long hospId) {
        LambdaQueryWrapper<IncomeCollection> wrapper = new LambdaQueryWrapper<IncomeCollection>()
                .eq(IncomeCollection::getYear, year)
                .eq(IncomeCollection::getMonth, month)
                .eq(IncomeCollection::getHospId, hospId);
        this.remove(wrapper);
    }

    /**
     * 归集后数据分页列表
     *
     * @param collectDTO {@link CollectDTO} 查询相关参数
     * @return {@link PageUtils}
     */
    @Override
    public PageUtils collectList(CollectDTO collectDTO) {
        // 分页数据初始化
        Integer current = collectDTO.getCurrent();
        current = (Objects.isNull(current) || current == 0) ? 0 : current - 1;
        Integer pageSize = collectDTO.getPageSize();
        pageSize = Objects.isNull(pageSize) ? 10 : pageSize;
        collectDTO.setCurrent(current * pageSize);
        collectDTO.setPageSize(pageSize);

        // 查询需要的数据 DB
        List<CollectedVO> list = baseMapper.getCollectList(collectDTO);
        int count = baseMapper.getCollectListCount(collectDTO);
        BigDecimal totalAmount = baseMapper.getShareLevelTotalAmount(collectDTO);
        BigDecimal responsibilityTotalAmount = baseMapper.getResponsibilityTotalAmount(collectDTO);

        return new PageUtils(list, count, pageSize, current + 1, totalAmount,responsibilityTotalAmount);
    }

    /**
     * 数据报表
     *
     * @param collectDTO
     * @return
     */
    @Override
    public CollectDataFormVO collectDataForm(CollectDTO collectDTO) {
        Long hospId = collectDTO.getHospId();
        String date = collectDTO.getDate();
        // 1. 得到所有的标题栏 责任中心代码
        List<CodeAndNameVO> responsibilities = this.baseMapper.getResponsibility(hospId, collectDTO.getResponsibilityCode(), date).stream().distinct().collect(Collectors.toList());
        if (responsibilities.isEmpty()) {
            return new CollectDataFormVO();
        }
        List<String> responsibilityCodes = responsibilities.stream().map(CodeAndNameVO::getCode).collect(Collectors.toList());
        responsibilities.add(0, new CodeAndNameVO("#", "#"));
        responsibilities.add(responsibilities.size(), new CodeAndNameVO("合计", "合计"));
        List<String> titleData = responsibilities.stream().map(CodeAndNameVO::getName).collect(Collectors.toList());
        Map<Integer, String> titleMap = new HashMap<>();
        for (int i = 0; i < titleData.size(); i++) {
            titleMap.put(i + 1, titleData.get(i));
        }

        // 所有的数据格式
        List<CodeAndNameVO> realData = this.baseMapper.getAccount(hospId, date);
        if (realData.isEmpty()) {
            return new CollectDataFormVO();
        }
        // realData [#,123,123,"合计"]
        List<Map<Integer, Object>> realDatas = new ArrayList<>();
        List<CodeAndNameVO> accounts = realData.stream().distinct().collect(Collectors.toList());

        for (CodeAndNameVO account : accounts) {
            Map<Integer, Object> map = new HashMap<>();
            for (int i = 0; i < responsibilities.size(); i++) {
                if (i == 0) {
                    map.put(i + 1, account.getName());
                    continue;
                } else if (i == responsibilities.size() - 1) {
                    // TODO: 2021/8/12 合计
                    BigDecimal amount = this.baseMapper.getCountAccountAndResponsibilities(hospId, date, account.getCode(), responsibilityCodes);
                    map.put(i + 1, amount);
                    continue;
                }
                List<IncomeCollection> list = this.list(
                        new LambdaQueryWrapper<IncomeCollection>().select(IncomeCollection::getAmount)
                                .eq(IncomeCollection::getHospId, hospId)
                                .eq(IncomeCollection::getYear, date.substring(0, 4))
                                .eq(IncomeCollection::getMonth, date.substring(4))
                                .eq(IncomeCollection::getAccountingCode, account.getCode())
                                .eq(IncomeCollection::getResponsibilityCode, responsibilities.get(i).getCode())
                );
                if (list.isEmpty()) {
                    map.put(i + 1, new BigDecimal("0.0000"));
                } else {
                    BigDecimal reduce = list.stream().map(IncomeCollection::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                    map.put(i + 1, reduce);
                }
            }
            realDatas.add(map);
        }
//
        // 合计栏计算
        // 长度一致的
        List<String> accountingCodes = realData.stream().map(CodeAndNameVO::getCode).collect(Collectors.toList());
        Map<Integer, Object> map = new HashMap<>();
        for (int i = 0; i < responsibilities.size(); i++) {
            if (i == 0) {
                map.put(i + 1, "合计");
                continue;
            } else if (i == responsibilities.size() - 1) {
                // 统计问题
                BigDecimal bigDecimal = this.baseMapper.getCountByResponsibilitiesAndAccounts(responsibilityCodes, accountingCodes, hospId, date);
                map.put(i + 1, bigDecimal);
                continue;
            }
            BigDecimal bigDecimal = this.baseMapper.getCountByResponseAndAccounts(responsibilities.get(i).getCode(), accountingCodes, hospId, date);
            map.put(i + 1, bigDecimal);
        }

        return new CollectDataFormVO(titleMap, realDatas, map);
    }

    /**
     * 获取某个时间的手机数据
     *
     * @param year   年
     * @param month  月
     * @param hospId 医院id
     * @return 收入数据id 可能给为null 调用者自行判断
     */
    @Override
    public List<IncomeCollection> getCollectionsByDate(int year, int month, Long hospId) {
        List<IncomeCollection> list = this.list(
                new LambdaQueryWrapper<IncomeCollection>()
                        .eq(IncomeCollection::getYear, year)
                        .eq(IncomeCollection::getMonth, month)
                        .eq(IncomeCollection::getHospId, hospId)
        );
        return list;
    }

    @Override
    public List<IncomeCollection> getCollectionsByDateAndResp(int year, int month, Long hospId, String responsibilityCode) {
        List<IncomeCollection> list = this.list(
                new LambdaQueryWrapper<IncomeCollection>()
                        .eq(IncomeCollection::getYear, year)
                        .eq(IncomeCollection::getMonth, month)
                        .eq(IncomeCollection::getHospId, hospId)
                        .eq(IncomeCollection::getResponsibilityCode, responsibilityCode)
        );
        return list;
    }

    @Override
    public List<IncomeCollection> getResponsibilitiesAccounts(int year, int month, Long hospId) {
        return baseMapper.getResponsibilitiesAccounts(year, month, hospId);
    }
}