package com.kcim.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kcim.common.constants.Constant;
import com.kcim.common.constants.NumberConstant;
import com.kcim.common.exception.CostException;
import com.kcim.common.util.BeanUtil;
import com.kcim.common.util.UserContext;
import com.kcim.dao.mapper.AccountingMapper;
import com.kcim.dao.model.Accounting;
import com.kcim.dao.model.User;
import com.kcim.dao.model.dto.AccountingEditDTO;
import com.kcim.dao.model.dto.AccountingSaveDTO;
import com.kcim.dao.repository.AccountingRepository;
import com.kcim.service.AccountingService;
import com.kcim.service.CenterService;
import com.kcim.service.CostShareParamService;
import com.kcim.vo.AccountVO;
import com.kcim.vo.DictDataVo;
import com.kcim.vo.SelectAccountingVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

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

import static com.kcim.common.constants.Constant.ACCOUNTING_TYPE;

@Slf4j
@Service("accountingService")
public class AccountingServiceImpl extends ServiceImpl<AccountingMapper, Accounting> implements AccountingService {

    private final CostShareParamService shareParamService;

    private final AccountingRepository accountingRepository;

    private final CenterService centerService;


    public AccountingServiceImpl(CostShareParamService shareParamService, AccountingRepository accountingRepository, CenterService centerService) {
        this.shareParamService = shareParamService;
        this.accountingRepository = accountingRepository;
        this.centerService = centerService;
    }

    /**
     * 获取会计科目列表按收入支出类型
     *
     * @param accountType  会计科目类型1.收入，2.支出
     * @param shareParamId 分摊id
     * @param filter
     * @return
     */
    @Override
    public List<AccountVO> getListByAccountType(Integer accountType, Integer shareParamId, Integer incomeGroutSetId, String filter) {
        // 1. 得到所有的会计科目
        List<Accounting> allAccount = accountingRepository.getList(accountType);


        if (CollUtil.isEmpty(allAccount)) {
            return Collections.emptyList();
        }
        // 所有的
//        List<AccountVO> all = list.stream().map(i -> BeanUtil.con(i, AccountVO.class)).collect(Collectors.toList());
        List<AccountVO> all = BeanUtil.convertList(allAccount, AccountVO.class);
        // 查询所有的已绑定的分摊参数
        if (Objects.nonNull(shareParamId)) {
            List<Long> ids = shareParamService.selectIsSelect(shareParamId);
            if (CollUtil.isNotEmpty(ids)) {
                all.forEach(i -> {
                    if (ids.contains(i.getId())) {
                        i.setIsShareParamSelect(true);
                    }
                });
            }
        }
        if (!StringUtils.isEmpty(filter)) {
            List<Accounting> filterAccount = accountingRepository.getList(accountType, filter);
            if (CollectionUtils.isEmpty(filterAccount)) {
                return Collections.emptyList();
            }
            List<AccountVO> filterVo = BeanUtil.convertList(filterAccount, AccountVO.class);
            Map<Long, List<AccountVO>> collect = filterVo.stream().collect(Collectors.groupingBy(AccountVO::getParentId));
            List<AccountVO> filterParent = collect.get(NumberConstant.ZERO_L);
            collect.remove(NumberConstant.ZERO_L);
            List<AccountVO> removeAccount = new ArrayList<>();
            all.forEach(f -> {
                if (!CollectionUtils.isEmpty(collect.get(f.getId()))) {
                    f.setChildren(getAccountTree(f, collect.get(f.getId())));
                } else {
                    removeAccount.add(f);
                }
            });
            if (!CollectionUtils.isEmpty(removeAccount)) {
                all.removeAll(removeAccount);
            }
            if (!CollectionUtils.isEmpty(filterParent)) {
                if (CollectionUtils.isEmpty(all)) {
                    all.addAll(filterParent);
                } else {
                    //查询条件不为空 对比 最上层是否有 costItems 的父类 没有 加回去 有 不添加
                    Map<Long, AccountVO> filterMap = filterParent.stream().collect(Collectors.toMap(AccountVO::getId, item -> item, (a, b) -> b));
                    List<AccountVO> filterAccountVo = all.stream().map(parent -> filterMap.get(parent.getId())).filter(Objects::nonNull).collect(Collectors.toList());
                    //去掉包含的 数据
                    filterParent.removeAll(filterAccountVo);
                    //剩下数据加回去
                    if (!CollectionUtils.isEmpty(filterParent)) {
                        all.addAll(filterParent);
                    }
                }
            }
            return all;
        } else {
            return all.stream().filter(o -> o.getParentId() == 0)
                    .peek(o -> o.setChildren(this.getAccountTree(o, all)))
                    .collect(Collectors.toList());
        }


    }

    /**
     * 递归处理
     *
     * @param accountVO
     * @param list
     * @return
     */
    private List<AccountVO> getAccountTree(AccountVO accountVO, List<AccountVO> list) {
        return list.stream().filter(o -> o.getParentId().equals(accountVO.getId()))
                .peek(o -> o.setChildren(this.getAccountTree(o, list)))
                .collect(Collectors.toList());
    }


    /**
     * 保存会计科目
     *
     * @param accountingSaveDTO
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveAccounting(AccountingSaveDTO accountingSaveDTO) {
        Long hospId = UserContext.getCurrentLoginHospId();
        // 校验会计科目代码
        this.checkAccountingCode(accountingSaveDTO.getAccountingCode(), hospId);

        // 新增逻辑
        Long parentId = accountingSaveDTO.getId();
        // 不是顶层的
        String allParentIds = "0";
        if (parentId != 0) {
            Accounting byId = this.getById(parentId);
            if (Objects.isNull(byId)) {
                throw new CostException(500, "上层级别会计科目已被移除");
            }
            // 如果父子节点的会计科目类型不一致，退出
            if (!byId.getAccountingType().equals(accountingSaveDTO.getAccountingType())) {
                throw new CostException(500, "会计科目类型要与上层一致");
            }
            String oldParentIds = byId.getAllParentIds();

            if ("0".equals(oldParentIds)) {
                allParentIds = parentId + "";
            } else {
                allParentIds = oldParentIds + "-" + parentId;
            }
        }
        Accounting accounting = BeanUtil.convertObj(accountingSaveDTO, Accounting.class);
        accounting.setHospId(hospId);
        accounting.setParentId(parentId);
        accounting.setAllParentIds(allParentIds);
        accounting.setCreateTime(System.currentTimeMillis());
        accounting.setId(null);
        // 如果是支出类型
        if (accountingSaveDTO.getAccountingType().equals(NumberConstant.TWO)) {
            Integer isBaseCode = accountingSaveDTO.getIsBaseCost();
            if (Objects.isNull(isBaseCode)) {
                throw new CostException(500, "支出类型需要选择是否固定支出成本");
            }
            accounting.setIsBaseCost(isBaseCode);
        }
        this.save(accounting);

    }


    private void checkAccountingCode(String code, Long hospId) {
        List<Accounting> list = this.baseMapper.selectList(
                new QueryWrapper<Accounting>().lambda().select(Accounting::getId)
                        .eq(Accounting::getAccountingCode, code)
                        .eq(Accounting::getHospId, hospId)
        );
        if (CollUtil.isNotEmpty(list)) {
            throw new CostException(500, "会计科目代码已存在，请重新生成");
        }
    }

    /**
     * 选择会计科目列表
     *
     * @return
     */
    @Override
    public List<SelectAccountingVO> selectAccounting() {
        Long hospId = UserContext.getCurrentLoginHospId();
        List<Accounting> list = this.list(
                new LambdaQueryWrapper<Accounting>().select(Accounting::getId, Accounting::getAccountingName, Accounting::getParentId, Accounting::getAllParentIds)
                        .eq(Accounting::getHospId, hospId)
        );
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 所有的
        List<SelectAccountingVO> all = list.stream().map(i -> {
            SelectAccountingVO vo = new SelectAccountingVO();
            vo.setValue(i.getId());
            vo.setLabel(i.getAccountingName());
            vo.setChildren(null);
            vo.setParentId(i.getParentId());
            vo.setAllParentIds(i.getAllParentIds());
            return vo;
        }).collect(Collectors.toList());
        // 顶层的
        List<SelectAccountingVO> parents = all.stream().filter(i -> i.getParentId() == 0).collect(Collectors.toList());
        List<SelectAccountingVO> accountVOS = new ArrayList<>();
        for (SelectAccountingVO parent : parents) {
            List<SelectAccountingVO> accountTree = this.getSelectAccountTree(parent, all);
            accountVOS.addAll(accountTree);
        }
        return accountVOS;
    }

    private List<SelectAccountingVO> getSelectAccountTree(SelectAccountingVO parent, List<SelectAccountingVO> all) {
        List<SelectAccountingVO> accountVOS = new ArrayList<>();
        accountVOS.add(parent);
        for (SelectAccountingVO account : all) {
            // 如果是父子关系
            if (parent.getValue().equals(account.getParentId())) {
                List<SelectAccountingVO> child = parent.getChildren();
                if (CollUtil.isEmpty(child)) {
                    child = new ArrayList<>();
                }
                child.add(account);
                parent.setChildren(child);
                // 处理子节点
                this.getSelectAccountTree(account, all);
            }
        }
        return accountVOS;
    }

    /**
     * 编辑科目代码
     *
     * @param accountingEditDTO
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void updateAccount(AccountingEditDTO accountingEditDTO) {
        // TODO: 2021/7/28 追踪溯源需求不满足
        Long id = accountingEditDTO.getId();
        Long hospId = UserContext.getCurrentLoginHospId();
//        this.checkAccountingCode(accountingEditDTO.getAccountingCode(), user.getHospId());
        Accounting one = this.baseMapper.selectOne(
                new QueryWrapper<Accounting>().lambda()
                        .eq(Accounting::getAccountingCode, accountingEditDTO.getAccountingCode())
                        .eq(Accounting::getHospId, hospId)
                        .last("limit 1")
        );

        Accounting byId = this.getById(id);
        if (Objects.isNull(byId)) {
            throw new CostException(500, "当前选中会计科目已被移除");
        }
        if (Objects.nonNull(one) && !byId.getAccountingCode().equals(one.getAccountingCode())) {
            throw new CostException(500, "会计科目代码已存在，请重新生成");
        }
        // 直接修改 TODO 不修改会计科目Code
        byId.setAccountingCode(byId.getAccountingCode());
        byId.setAccountingName(accountingEditDTO.getAccountingName());
        byId.setCreateTime(System.currentTimeMillis());
        byId.setControlType(accountingEditDTO.getControlType());
        byId.setType(accountingEditDTO.getType());
        byId.setTypeName(accountingEditDTO.getTypeName());
        byId.setMedicalType(accountingEditDTO.getMedicalType());
        if (accountingEditDTO.getAccountType().equals(NumberConstant.TWO)) {
            byId.setCostType(accountingEditDTO.getCostType());
        }
//        byId.setAccountingType(accountingEditDTO.getAccountingType());
        this.updateById(byId);
//        this.removeById(id);
//        Accounting accounting = BeanUtil.convertObj(byId, Accounting.class);
//        saveAccount(accountingEditDTO, user, byId, accounting);
//        List<Accounting> list = new ArrayList<>();
//        this.getAndAllChild(Arrays.asList(id), user.getHospId(), list);
//        log.info("list:{}", list);
//        if (CollUtil.isEmpty(list)) {
//            return;
//        }
//        // 第一个子节点
//        List<Accounting> childList = list.stream().filter(i -> i.getParentId().equals(id)).collect(Collectors.toList());
//        if (CollUtil.isEmpty(childList)) {
//            throw new CostException(500, "数据异常");
//        }
//        childList.forEach(i -> {
//            i.setParentId(accounting.getId());
//            String allParentIds = setAllParentIds(byId);
//            i.setAllParentIds(allParentIds);
//        });
//        this.updateBatchById(childList);
//
//
    }

//    private String setAllParentIds(Accounting byId) {
//        Integer parentId = byId.getParentId();
//        // 不是顶层的
//        String allParentIds = "0";
//        if (parentId != 0) {
//            String oldParentIds = byId.getAllParentIds();
//            if ("0".equals(oldParentIds)) {
//                allParentIds = parentId + "";
//            } else {
//                allParentIds = oldParentIds + "-" + parentId;
//            }
//        }
//        return allParentIds;
//    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveAccount(AccountingEditDTO accountingEditDTO, User user, Accounting byId, Accounting accounting) {
        this.checkAccountingCode(accountingEditDTO.getAccountingCode(), user.getHospId());
        accounting.setAccountingCode(accounting.getAccountingCode());
        accounting.setCreateTime(System.currentTimeMillis());
        // 新增逻辑
        Long parentId = byId.getParentId();
        // 不是顶层的
        String allParentIds = "0";
        if (parentId != 0) {
            String oldParentIds = byId.getAllParentIds();
            if ("0".equals(oldParentIds)) {
                allParentIds = parentId + "";
            } else {
                allParentIds = oldParentIds + "-" + parentId;
            }
        }
        accounting.setHospId(user.getHospId());
        accounting.setParentId(parentId);
        accounting.setAllParentIds(allParentIds);
        accounting.setCreateTime(System.currentTimeMillis());
        this.save(accounting);
    }

    /**
     * 得到自己还有所有的子节点
     *
     * @param ids
     * @param accounts
     * @return
     */
    private List<Accounting> getAndAllChild(List<Long> ids, Long hospId, List<Accounting> accounts) {

        List<Accounting> list = this.list(
                new LambdaQueryWrapper<Accounting>()
                        .in(Accounting::getParentId, ids).eq(Accounting::getHospId, hospId)
        );
        if (CollUtil.isEmpty(list)) {
            return accounts;
        }
        accounts.addAll(list);
        this.getAndAllChild(list.stream().map(Accounting::getId).collect(Collectors.toList()), hospId, accounts);
        return accounts;
    }

    /**
     * 删除
     *
     * @param id
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deleteAccount(Long id) {
        List<Accounting> list = new ArrayList<>();
        List<Accounting> andAllChild = this.getAndAllChild(Collections.singletonList(id), UserContext.getCurrentLoginHospId(), list);
        if (CollUtil.isEmpty(andAllChild)) {
            this.removeById(id);
        }
        List<Long> collect = andAllChild.stream().map(Accounting::getId).collect(Collectors.toList());
        collect.add(id);
        this.removeByIds(collect);
    }

    /**
     * 通过code得到对应的会计科目对象 需要调用者自己判断空
     *
     * @param accountCode 会计科目代码
     * @return {@link Accounting} 可能为空 需要调用者自行判断
     */
    @Override
    public Accounting getByCode(String accountCode, Long hospId) {
        if (StrUtil.isEmpty(accountCode)) {
            throw new CostException("参数异常，accountCode 不能为空");
        }
        Accounting one = this.getOne(
                new LambdaQueryWrapper<Accounting>()
                        .eq(Accounting::getAccountingCode, accountCode)
                        .eq(Accounting::getHospId, hospId)
                        .last(Constant.LIMIT)
        );
        return one;
    }

    @Override
    public Object getAccountTypeDict(Integer type) {
        DictDataVo dict = centerService.getDict(ACCOUNTING_TYPE);
        if(Objects.isNull(dict)){
            throw new CostException("未维护会计科目类别字典");
        }
        List<DictDataVo> dataVoList = dict.getDataVoList();
        if(CollectionUtils.isEmpty(dataVoList)){
            throw new CostException("未维护会计科目类别字典");
        }
        if(type.equals(NumberConstant.ONE)){
            //收入
            return dataVoList.stream().filter(f -> f.getValue().equals(NumberConstant.ONE_S)).collect(Collectors.toList());
        }else {
            //成本
            return dataVoList.stream().filter(f -> f.getValue().equals(NumberConstant.TWO_S)).collect(Collectors.toList());
        }
    }

    public List<Accounting> getAccountList(Long hospId, List<String> accountList){
        return this.list(
                new LambdaQueryWrapper<Accounting>().select(Accounting::getId,Accounting::getAccountingCode, Accounting::getAccountingName)
                        .eq(Accounting::getHospId, hospId).in(Accounting::getAccountingCode,accountList)
        );
    }
}