package com.prolog.rdc.tms.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.collect.Lists;
import com.prolog.framework.common.message.RestMessage;
import com.prolog.framework.core.exception.BizException;
import com.prolog.framework.core.exception.ParameterException;
import com.prolog.framework.core.exception.PrologException;
import com.prolog.framework.core.pojo.Page;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Order;
import com.prolog.framework.core.restriction.Restriction;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.framework.dao.util.PageUtils;
import com.prolog.product.core.easyexcel.util.ExcelUtils;
import com.prolog.product.core.framework.entity.UserEntity;
import com.prolog.product.core.framework.util.PlgFxUsrUtils;
import com.prolog.product.core.tools.util.JsonMapper;
import com.prolog.product.core.tools.util.ToolKits;
import com.prolog.rdc.tms.constant.BmsConstant;
import com.prolog.rdc.tms.constant.RabbitMQConstant;
import com.prolog.rdc.tms.dao.mapper.ExpenseAccountMapper;
import com.prolog.rdc.tms.dao.mapper.ExpenseManagementMapper;
import com.prolog.rdc.tms.model.dto.ExpenseManagementQueryDTO;
import com.prolog.rdc.tms.model.dto.ExpenseManagmentTemplateDTO;
import com.prolog.rdc.tms.model.dto.basicConfigure.StandardTemplateDetailsListDTO;
import com.prolog.rdc.tms.model.dto.basicData.BaseFeeDTO;
import com.prolog.rdc.tms.model.entity.BmsExpenseManagementModel;
import com.prolog.rdc.tms.model.entity.ExpenseAccountModel;
import com.prolog.rdc.tms.model.vo.ExpenseManagementImportVO;
import com.prolog.rdc.tms.model.vo.ExpenseManagementQueryVO;
import com.prolog.rdc.tms.model.vo.ExpenseManagementVO;
import com.prolog.rdc.tms.model.vo.StatusUpdate;
import com.prolog.rdc.tms.model.vo.basicConfigure.ImportLogAddVO;
import com.prolog.rdc.tms.model.vo.basicData.SettlementTypeVO;
import com.prolog.rdc.tms.rmi.feign.MaindataFeign;
import com.prolog.rdc.tms.service.ExpenseManagementService;
import com.prolog.rdc.tms.util.FileUtils;
import com.prolog.rdc.tms.util.ImportUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.plugin.PluginException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ExpenseManagementServiceImpl implements ExpenseManagementService {

    private final Logger logger = LoggerFactory.getLogger(ExpenseManagementServiceImpl.class);

    @Autowired
    private ExpenseManagementMapper mapper;
    @Autowired
    private MaindataFeign maindataTmsResource;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private ExpenseAccountMapper expenseAccountMapper;

    @Override
    public void save(ExpenseManagementVO vo) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        // 判断编号是否存在
        Criteria criteria = Criteria.forClass(BmsExpenseManagementModel.class);
        Restriction r1 = ObjectUtil.isEmpty(vo.getExpenseCode()) ? null : Restrictions.eq("expenseCode", vo.getExpenseCode());
        Restriction r2 = ObjectUtil.isEmpty(vo.getExpenseName()) ? null : Restrictions.eq("expenseName", vo.getExpenseName());
        Restriction r3 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
        Restriction r4 = Restrictions.or(r1, r2);
        criteria.setRestriction(Restrictions.and(r3, r4));
        List<BmsExpenseManagementModel> models = mapper.findByCriteria(criteria);
        if (ObjectUtil.isNotEmpty(models)) {
            throw new PluginException("费用管理已经存在，请重新输入");
        }
        BmsExpenseManagementModel model = new BmsExpenseManagementModel();
        BeanUtils.copyProperties(vo, model);
        model.setCreateperson(user.getNickname());
        model.setCreatetime(DateUtil.date());
        model.setUpdateperson(user.getNickname());
        model.setUpdatetime(DateUtil.date());
        model.setEnterpriseId(user.getEnterpriseId());
        model.setEnterpriseName(user.getEnterpriseName());
        model.setExpenseType(BmsConstant.SOURCE_USER);
        model.setStatus(1);
        mapper.save(model);
    }

    @Override
    public Page<ExpenseManagementQueryDTO> findList(ExpenseManagementQueryVO vo) {
        UserEntity user = PlgFxUsrUtils.getUserEntity();

        Criteria criteria = Criteria.forClass(BmsExpenseManagementModel.class);
        Restriction r1 = ObjectUtil.isEmpty(vo.getExpenseCode()) ? null : Restrictions.like("expenseCode", "%" + vo.getExpenseCode() + "%");
        Restriction r2 = ObjectUtil.isEmpty(vo.getExpenseName()) ? null : Restrictions.like("expenseName", "%" + vo.getExpenseName() + "%");
        Restriction r3 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
        Restriction r4 = ObjectUtil.isEmpty(vo.getExcludeExpenseName()) ? null : Restrictions.ne("expenseName", vo.getExcludeExpenseName() );
        Restriction r5 = ObjectUtil.isEmpty(vo.getExpenseClassify()) ? null : Restrictions.eq("expenseClassify", vo.getExpenseClassify() );
        Restriction r6 = ObjectUtil.isEmpty(vo.getStatus()) ? null : Restrictions.eq(BmsExpenseManagementModel::getStatus, vo.getStatus());
        criteria.setRestriction(Restrictions.and(r1, r2, r3,r4,r5,r6));
        criteria.setOrder(Order.newInstance().desc("updatetime"));

        PageUtils.startPage(vo.getPageNum(), vo.getPageSize());
        List<BmsExpenseManagementModel> list = mapper.findByCriteria(criteria);

        List<ExpenseManagementQueryDTO> dtos = Lists.newArrayList();
        for (BmsExpenseManagementModel expenseAccountModel : list) {
            ExpenseManagementQueryDTO dto = new ExpenseManagementQueryDTO();
            BeanUtils.copyProperties(expenseAccountModel, dto);
            dto.setStatusName(expenseAccountModel.getStatus() == 1 ? "启用" : "停用");
            dto.setExpenseTypeName(expenseAccountModel.getExpenseType() == 1 ? "用户" : "系统");
            dto.setExpenseClassifyName(expenseAccountModel.getExpenseClassify() == 1 ? "收入" : "支出");
            dtos.add(dto);
        }
        Page<ExpenseManagementQueryDTO> page = PageUtils.getPage(dtos);
        if (list instanceof com.github.pagehelper.Page) {
            com.github.pagehelper.Page<BmsExpenseManagementModel> p0 = (com.github.pagehelper.Page<BmsExpenseManagementModel>) list;
            page.setPageCount(p0.getPages());
            page.setPageNum(p0.getPageNum());
            page.setPageSize(p0.getPageSize());
            page.setTotalCount(p0.getTotal());
            page.setStartRow(p0.getStartRow());
            page.setEndRow(p0.getEndRow());
        }
        return page;
    }

    @Override
    // 数据类型 1:货主 2:承运商 3:司机
    public List<ExpenseManagementQueryDTO> findListNoPage(String code, Integer dataType) {
        if (ObjectUtil.isEmpty(dataType)) {
            throw new PrologException("数据类型不为空");
        }
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        List<ExpenseManagementQueryDTO> dtoList = Lists.newArrayList();
        if (ObjectUtil.isNotEmpty(code)) {
            // 1.查询当前用户现在的基础费用
            RestMessage<List<BaseFeeDTO>> message = this.findBaseFee("", code, user, dataType);
            if (ObjectUtil.isNotEmpty(message) && ObjectUtil.isNotEmpty(message.getData())) {
                List<BaseFeeDTO> data = message.getData();
                for (BaseFeeDTO datum : data) {
                    ExpenseManagementQueryDTO dto = new ExpenseManagementQueryDTO();
                    BeanUtils.copyProperties(datum, dto);
                    dtoList.add(dto);
                }
            }
        } else {
            // 查询所有的基础计费
            Criteria criteria = Criteria.forClass(BmsExpenseManagementModel.class);
            Restriction r1 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
            Restriction r2 = Restrictions.eq("status", 1);
            Restriction r3 = null;
            if (1 == dataType.intValue()) {
                r3 = Restrictions.eq(BmsExpenseManagementModel::getExpenseClassify, 1);
            } else {
                r3 = Restrictions.eq(BmsExpenseManagementModel::getExpenseClassify, 2);
            }
            criteria.setRestriction(Restrictions.and(r1, r2, r3));
            criteria.setOrder(Order.newInstance().desc("createtime"));
            List<BmsExpenseManagementModel> feeModels = mapper.findByCriteria(criteria);
            if (ObjectUtil.isNotEmpty(feeModels)) {
                for (BmsExpenseManagementModel feeModel : feeModels) {
                    ExpenseManagementQueryDTO expenseManagementModel = new ExpenseManagementQueryDTO();
                    BeanUtils.copyProperties(feeModel, expenseManagementModel);
                    dtoList.add(expenseManagementModel);
                }
            }
        }
        return dtoList;
    }

    private RestMessage<List<BaseFeeDTO>> findBaseFee(String expenseCode, String code, UserEntity user, Integer dataType) {
        SettlementTypeVO settlementTypeVO = new SettlementTypeVO();
        settlementTypeVO.setCode(code);
        settlementTypeVO.setDataType(dataType);
        settlementTypeVO.setExpenseCode(expenseCode);
        settlementTypeVO.setEnterpriseId(user.getEnterpriseId());
        return maindataTmsResource.feeCode(settlementTypeVO);
    }


    @Override
    public ExpenseManagementQueryDTO detail(Long id) {
        if (ObjectUtil.isEmpty(id)) {
            throw new PluginException("查询ID不能为空");
        }
        ExpenseManagementQueryDTO queryDTO = new ExpenseManagementQueryDTO();
        BmsExpenseManagementModel managementModel = mapper.findById(id, BmsExpenseManagementModel.class);
        BeanUtils.copyProperties(managementModel, queryDTO);
        queryDTO.setExpenseClassifyName(Objects.equals(managementModel.getExpenseClassify(), 1) ? "收入" : "支出");
        queryDTO.setExpenseTypeName(Objects.equals(managementModel.getExpenseType(), 1) ? "收入" : "支出");
        return queryDTO;
    }

    @Override
    public void update(ExpenseManagementVO vo) {
        if (ObjectUtil.isEmpty(vo.getId())) {
            throw new PluginException("行ID不能为空");
        }

        UserEntity user = PlgFxUsrUtils.getUserEntity();

        // 判断编号是否存在
        Criteria criteria = Criteria.forClass(BmsExpenseManagementModel.class);
        Restriction r1 = ObjectUtil.isEmpty(vo.getExpenseCode()) ? null : Restrictions.eq("expenseCode", vo.getExpenseCode());
        Restriction r2 = ObjectUtil.isEmpty(vo.getExpenseName()) ? null : Restrictions.eq("expenseName", vo.getExpenseName());
        Restriction r3 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
        Restriction r4 = Restrictions.ne("id", vo.getId());
        Restriction r5 = Restrictions.or(r1, r2);
        criteria.setRestriction(Restrictions.and(r3, r4, r5));
        List<BmsExpenseManagementModel> models = mapper.findByCriteria(criteria);
        if (ObjectUtil.isNotEmpty(models)) {
            throw new PluginException("费用管理已经存在，请重新输入");
        }

        BmsExpenseManagementModel managementModel = mapper.findById(vo.getId(), BmsExpenseManagementModel.class);
        BeanUtils.copyProperties(vo, managementModel);
        managementModel.setUpdateperson(user.getNickname());
        managementModel.setUpdatetime(DateUtil.date());
        mapper.update(managementModel);
    }

    @Override
    public void delete(Long[] ids) {
        if (ObjectUtil.isEmpty(ids)) {
            throw new RuntimeException("请求参数不能为空");
        }
        // 判断是否启用
        for (Long id : ids) {
            BmsExpenseManagementModel managementModel = mapper.findById(id, BmsExpenseManagementModel.class);
            if (managementModel.getStatus() == 1) {
                throw new PluginException(String.format("费用管理[%s],是启用状态不能删除", managementModel.getExpenseName()));
            }

            mapper.deleteById(id, BmsExpenseManagementModel.class);
        }
    }

    @Override
    public void updateStatus(List<StatusUpdate> list) {
        logger.info("- > updateStatus vo={}", JsonMapper.toJsonString(list));
        // 1 校验数据
        if (ObjectUtil.isEmpty(list)) {
            throw new RuntimeException("请求参数不能为空");
        }
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        for (StatusUpdate statusUpdate : list) {
            BmsExpenseManagementModel managementModel = mapper.findById(statusUpdate.getId(), BmsExpenseManagementModel.class);
            managementModel.setStatus(statusUpdate.getStatus());
            managementModel.setUpdateperson(user.getNickname());
            managementModel.setUpdatetime(DateUtil.date());
            mapper.update(managementModel);
        }
    }

    @Override
    public List<ExpenseManagmentTemplateDTO> exportExpense(HttpServletResponse response, ExpenseManagementQueryVO dto) {
        List<ExpenseManagmentTemplateDTO> list = Lists.newArrayList();
        if (CollectionUtil.isNotEmpty(dto.getIds())) {
            // 通过ids 查询商品信息
            Criteria criteria = Criteria.forClass(BmsExpenseManagementModel.class);
            Restriction r1 = Restrictions.in("id", dto.getIds().toArray());
            criteria.setRestriction(Restrictions.and(r1));
            List<BmsExpenseManagementModel> models = mapper.findByCriteria(criteria);
            return this.copy(models, list);
        }
        if (dto.getPageNum() != 0 && dto.getPageSize() != 0) {
            PageUtils.startPage(dto.getPageNum(), dto.getPageSize());
        }
        Criteria criteria = Criteria.forClass(BmsExpenseManagementModel.class);
        Restriction r1 = Restrictions.like("expenseCode", "%" + dto.getExpenseCode() + "%");
        Restriction r2 = Restrictions.like("expenseName", "%" + dto.getExpenseName() + "%");
        criteria.setRestriction(Restrictions.and(r1, r2));
        List<BmsExpenseManagementModel> models = mapper.findByCriteria(criteria);
        return this.copy(models, list);
    }

    @Override
    public void importExpense(MultipartFile file) throws IOException {
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        if (ObjectUtil.isEmpty(user)) {
            throw new PluginException("当前人员没有操作权限");
        }
        // 导入文件判断
        FileUtils.multipartFileJudge(file);
        // 导入批次
        RestMessage<String> code = maindataTmsResource.getCodeNumberByCode(BmsConstant.IMPORT_BATCHNO, "L");
        if (ObjectUtil.isEmpty(code.getData())) {
            throw new PluginException("获取批次ID失败");
        }
        // 导入日志对象
        ImportLogAddVO logAddVO = new ImportLogAddVO();
        logAddVO.setImportObjNo(BmsConstant.EXPENSE_ACCOUNT_IMPORT);
        logAddVO.setImportBatchNumber(code.getData());
        // 导入统计
        int count = 0;
        try {
            // 导入执行
            count = this.importProcess(file, code.getData(), user);
            logAddVO.setStatus(BmsConstant.IMPORT_STATUS_YES);
            logAddVO.setSuccessNum(count);
            logAddVO.setFailNum(0);
            maindataTmsResource.addImportLogExecute(logAddVO);
        } catch (Exception e) {
            Class<ExpenseManagementImportVO> importVOClass = ExpenseManagementImportVO.class;
            List<ExpenseManagementImportVO> accountImportVOS = ExcelUtils.read(file, importVOClass);

            logAddVO.setStatus(BmsConstant.IMPORT_STATUS_NO);
            logAddVO.setSuccessNum(0);
            logAddVO.setFailNum(accountImportVOS.size());
            logAddVO.setFailReason(e.getMessage());
            // 为了避免导入失败时导入日志的保存事务也被回滚,所以采用消息队列异步保存导入失败的日志
            amqpTemplate.convertAndSend(RabbitMQConstant.EXPENSE_MANAGEMENT_IMPORT, logAddVO);
            e.printStackTrace();
            throw new ParameterException(e.getMessage());
        }

    }

    private int importProcess(MultipartFile file, String data, UserEntity user) {
        Date date = new Date();
        // 费用管理
        List<BmsExpenseManagementModel> modelList = Lists.newArrayList();
        Properties properties = new Properties();
        // 待转换的对象
        Class<ExpenseManagementImportVO> importVOClass = ExpenseManagementImportVO.class;
        // 标准模板查询
        RestMessage<List<StandardTemplateDetailsListDTO>> restMessage = maindataTmsResource.listStandardTemplateDetailsExecute(BmsConstant.EXPENSE_MANAGEMENT);
        if (!restMessage.isSuccess()) {
            throw new ParameterException("标准模板详情查询失败:" + restMessage.getMessage());
        }

        List<StandardTemplateDetailsListDTO> standardTemplateDetailsListDTOs = restMessage.getData();
        ToolKits.isParamNull(standardTemplateDetailsListDTOs, "运单标准模板未配置");

        for (StandardTemplateDetailsListDTO standardTemplateDetailsListDTO : standardTemplateDetailsListDTOs) {
            // 属性字段和注解属性字段值 键值对
            properties.setProperty(standardTemplateDetailsListDTO.getStdColNo(), standardTemplateDetailsListDTO.getCustColName());
        }
        // @ExcelProperty注解属性修改
        ImportUtils.setHeaderValue(properties, importVOClass);
        // EXCEL转对象
        List<ExpenseManagementImportVO> managementImportVOS;
        try {
            managementImportVOS = ExcelUtils.read(file, importVOClass);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BizException("EXCEL转对象失败:" + e.getMessage());
        }

        ToolKits.isParamNull(managementImportVOS, "EXCEL文件内容为空,请重新导入");
        log.info("商品导入详情:{}", JsonMapper.toJson(managementImportVOS));

        int i = 1;
        for (ExpenseManagementImportVO managementImportVO : managementImportVOS) {
            if (ToolKits.isEmpty(managementImportVO.getExpenseCode())) {
                throw new PrologException("第" + i + "行导入数据,费用编码不能为空");
            }
            if (ToolKits.isEmpty(managementImportVO.getExpenseName())) {
                throw new PrologException("第" + i + "行导入数据,费用名称不能为空");
            }
            if (ToolKits.isEmpty(managementImportVO.getExpenseAccountName())) {
                throw new PrologException("第" + i + "行导入数据,费用分类不能为空");
            }
            if (ToolKits.isEmpty(managementImportVO.getExpenseAccountCode())) {
                throw new PrologException("第" + i + "行导入数据,费用分类不能为空");
            }
            i++;
        }
        // 验证导入文件中费用编号是否重复
        Map<String, Long> codeMap = managementImportVOS.stream().collect(Collectors.groupingBy(ExpenseManagementImportVO::getExpenseCode, Collectors.counting()));
        for (Map.Entry<String, Long> longEntry : codeMap.entrySet()) {
            if (longEntry.getValue() > 1) {
                throw new BizException(String.format("导入文件中，费用编号[%s]重复", longEntry.getKey()));
            }
        }
        // 验证导入文件中费用名称是否重复
        Map<String, Long> nameMap = managementImportVOS.stream().collect(Collectors.groupingBy(ExpenseManagementImportVO::getExpenseName, Collectors.counting()));
        for (Map.Entry<String, Long> longEntry : nameMap.entrySet()) {
            if (longEntry.getValue() > 1) {
                throw new ParameterException(String.format("导入文件中，费用名称[%s]重复", longEntry.getKey()));
            }
        }
        AtomicInteger num = new AtomicInteger(0);
        for (ExpenseManagementImportVO itemImportStdVO : managementImportVOS) {
            // 费用科目
            Criteria criteria = Criteria.forClass(ExpenseAccountModel.class);
            Restriction r1 = Restrictions.eq("expenseAccountName", itemImportStdVO.getExpenseAccountName());
            criteria.setRestriction(Restrictions.and(r1));
            List<ExpenseAccountModel> accountModels = expenseAccountMapper.findByCriteria(criteria);
            if (ObjectUtil.isEmpty(accountModels)) {
                throw new PluginException("费用科目信息不存在");
            }
            ExpenseAccountModel expenseAccountModel = accountModels.get(0);
            BmsExpenseManagementModel bmsExpenseManagementModel = new BmsExpenseManagementModel();
            BeanUtils.copyProperties(itemImportStdVO, bmsExpenseManagementModel);
            bmsExpenseManagementModel.setExpenseClassify(expenseAccountModel.getExpenseClassify());
            bmsExpenseManagementModel.setExpenseAccountCode(expenseAccountModel.getExpenseCode());
            bmsExpenseManagementModel.setCreatetime(date);
            bmsExpenseManagementModel.setCreateperson(user.getNickname());
            bmsExpenseManagementModel.setUpdateperson(user.getNickname());
            bmsExpenseManagementModel.setUpdatetime(date);
            bmsExpenseManagementModel.setEnterpriseId(user.getEnterpriseId());
            bmsExpenseManagementModel.setEnterpriseName(user.getEnterpriseName());
            bmsExpenseManagementModel.setStatus(1);
            mapper.save(bmsExpenseManagementModel);

            modelList.add(bmsExpenseManagementModel);

            num.getAndIncrement();
        }
        return modelList.size();
    }

    private List<ExpenseManagmentTemplateDTO> copy(List<BmsExpenseManagementModel> models, List<ExpenseManagmentTemplateDTO> list) {
        for (BmsExpenseManagementModel model : models) {
            ExpenseManagmentTemplateDTO dto = new ExpenseManagmentTemplateDTO();
            BeanUtils.copyProperties(model, dto);
            list.add(dto);
        }
        return list;
    }
}