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.ExpenseAccountQueryDTO;
import com.prolog.rdc.tms.model.dto.ExpenseAccountTemplateDTO;
import com.prolog.rdc.tms.model.dto.basicConfigure.StandardTemplateDetailsListDTO;
import com.prolog.rdc.tms.model.entity.BmsExpenseManagementModel;
import com.prolog.rdc.tms.model.entity.ExpenseAccountModel;
import com.prolog.rdc.tms.model.vo.ExpenseAccountImportVO;
import com.prolog.rdc.tms.model.vo.ExpenseAccountQueryVO;
import com.prolog.rdc.tms.model.vo.ExpenseAccountVO;
import com.prolog.rdc.tms.model.vo.StatusUpdate;
import com.prolog.rdc.tms.model.vo.basicConfigure.ImportLogAddVO;
import com.prolog.rdc.tms.rmi.feign.MaindataFeign;
import com.prolog.rdc.tms.service.ExpenseAccountService;
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.transaction.annotation.Transactional;
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 ExpenseAccountServiceImpl implements ExpenseAccountService {

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

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

	@Override
	public void createExpenseAccount(ExpenseAccountVO vo) {
		UserEntity user = PlgFxUsrUtils.getUserEntity();

		// 判断编号是否存在
		Criteria criteria = Criteria.forClass(ExpenseAccountModel.class);
		Restriction r1 = ObjectUtil.isEmpty(vo.getExpenseCode()) ? null : Restrictions.eq("expenseCode", vo.getExpenseCode());
		Restriction r2 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
		criteria.setRestriction(Restrictions.and(r1, r2));
		List<ExpenseAccountModel> models = mapper.findByCriteria(criteria);
		if (ObjectUtil.isNotEmpty(models)) {
			throw new PluginException("费用科目已经存在，请重新输入");
		}
		// 判断名称是否存在
		Criteria aCriteria = Criteria.forClass(ExpenseAccountModel.class);
		Restriction r3 = ObjectUtil.isEmpty(vo.getExpenseName()) ? null : Restrictions.eq("expenseName", vo.getExpenseName());
		Restriction r4 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
		aCriteria.setRestriction(Restrictions.and(r3, r4));
		List<ExpenseAccountModel> accountModels = mapper.findByCriteria(aCriteria);
		if (ObjectUtil.isNotEmpty(accountModels)) {
			throw new PluginException("费用科目名称已经存在，请重新输入");
		}
		ExpenseAccountModel expenseAccountModel = new ExpenseAccountModel();
		BeanUtils.copyProperties(vo, expenseAccountModel);
		expenseAccountModel.setCreateperson(user.getNickname());
		expenseAccountModel.setCreatetime(DateUtil.date());
		expenseAccountModel.setUpdateperson(user.getNickname());
		expenseAccountModel.setUpdatetime(DateUtil.date());
		expenseAccountModel.setEnterpriseId(user.getEnterpriseId());
		expenseAccountModel.setEnterpriseName(user.getEnterpriseName());
		expenseAccountModel.setExpenseType(BmsConstant.SOURCE_ADD);
		expenseAccountModel.setStatus(1);
		mapper.save(expenseAccountModel);
	}

	@Override
	public Page<ExpenseAccountQueryDTO> queryExpenseAccount(ExpenseAccountQueryVO vo) {
		UserEntity user = PlgFxUsrUtils.getUserEntity();

		Criteria criteria = Criteria.forClass(ExpenseAccountModel.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.isNull(vo.getStatus()) ? null : Restrictions.eq(ExpenseAccountModel::getStatus, vo.getStatus());
		criteria.setRestriction(Restrictions.and(r1, r2, r3, r4));
		criteria.setOrder(Order.newInstance().desc("updatetime"));

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

		List<ExpenseAccountQueryDTO> dtos = Lists.newArrayList();
		for (ExpenseAccountModel expenseAccountModel : list) {
			ExpenseAccountQueryDTO dto = new ExpenseAccountQueryDTO();
			BeanUtils.copyProperties(expenseAccountModel, dto);
			dto.setStatusName(expenseAccountModel.getStatus() == 1?"启用":"停用");
			dto.setExpenseClassifyName(Objects.equals(dto.getExpenseClassify(), 1) ? "收入" : "支出");
			if (BmsConstant.SOURCE_ADD == 1) {
				dto.setExpenseTypeName("用户");
			} else {
				dto.setExpenseTypeName("系统");
			}
			dtos.add(dto);
		}
		Page<ExpenseAccountQueryDTO> page = PageUtils.getPage(dtos);
		if (list instanceof com.github.pagehelper.Page) {
			com.github.pagehelper.Page<ExpenseAccountModel> p0 = (com.github.pagehelper.Page<ExpenseAccountModel>) 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
	public ExpenseAccountQueryDTO detail(Long id) {
		if (ObjectUtil.isEmpty(id)) {
			throw new PluginException("查询ID不能为空");
		}
		ExpenseAccountQueryDTO accountQueryDTO = new ExpenseAccountQueryDTO();
		ExpenseAccountModel accountModel = mapper.findById(id, ExpenseAccountModel.class);
		BeanUtils.copyProperties(accountModel, accountQueryDTO);
		accountQueryDTO.setExpenseClassifyName(Objects.equals(accountModel.getExpenseClassify(), 1) ? "收入" : "支出");
		return accountQueryDTO;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void delete(Long[] ids) {
		UserEntity user = PlgFxUsrUtils.getUserEntity();
		if (ObjectUtil.isEmpty(ids)) {
			throw new RuntimeException("请求参数不能为空");
		}
		// 判断是否启用
		for (Long id : ids) {
			ExpenseAccountModel accountModel = mapper.findById(id, ExpenseAccountModel.class);
			if (accountModel.getStatus() == 1) {
				throw new PluginException(String.format("费用科目[%s],是启用状态不能删除", accountModel.getExpenseName()));
			}
			String expenseCode = accountModel.getExpenseCode();
			Criteria criteria = Criteria.forClass(BmsExpenseManagementModel.class);
			Restriction r1 = Restrictions.eq("expenseAccountCode", expenseCode);
			Restriction r2 = Restrictions.eq("status", 1);
			Restriction r3 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
			criteria.setRestriction(Restrictions.and(r1, r2,r3));
			List<BmsExpenseManagementModel> managementModels = managementMapper.findByCriteria(criteria);
			if (ObjectUtil.isNotEmpty(managementModels)) {
				throw new PluginException("费用科目存在启用状态的费用，请先停用费用");
			}
			mapper.deleteById(id, ExpenseAccountModel.class);
		}
	}

	@Override
	public void updateStatus(List<StatusUpdate> vo) {
		logger.info("- > updateStatus vo={}", JsonMapper.toJsonString(vo));
		// 1 校验数据
		if (ObjectUtil.isEmpty(vo)) {
			throw new RuntimeException("请求参数不能为空");
		}

		UserEntity user = PlgFxUsrUtils.getUserEntity();
		for (StatusUpdate statusUpdate : vo) {
			ExpenseAccountModel accountModel = mapper.findById(statusUpdate.getId(), ExpenseAccountModel.class);
			// 查询费用管理下对应的费用科目是否停用
			if (ObjectUtil.isEmpty(accountModel)) {
				throw new PluginException("查询费用科目信息不存在");
			}
			String expenseCode = accountModel.getExpenseCode();
			Criteria criteria = Criteria.forClass(BmsExpenseManagementModel.class);
			Restriction r1 = Restrictions.eq("expenseAccountCode", expenseCode);
			Restriction r2 = Restrictions.eq("status", 1);
			Restriction r3 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
			criteria.setRestriction(Restrictions.and(r1, r2,r3));
			List<BmsExpenseManagementModel> managementModels = managementMapper.findByCriteria(criteria);
			if (ObjectUtil.isNotEmpty(managementModels) && statusUpdate.getStatus() == 0) {
				throw new PluginException("费用科目存在启用状态的费用，请先停用费用");
			}
			accountModel.setStatus(statusUpdate.getStatus());
			accountModel.setUpdateperson(user.getNickname());
			accountModel.setUpdatetime(DateUtil.date());
			this.mapper.update(accountModel);
		}
	}

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

		UserEntity user = PlgFxUsrUtils.getUserEntity();

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

		ExpenseAccountModel model = mapper.findById(vo.getId(), ExpenseAccountModel.class);
		BeanUtils.copyProperties(vo, model);
		model.setUpdateperson(user.getNickname());
		model.setUpdatetime(DateUtil.date());
		model.setStatus(1);
		mapper.update(model);
	}

	@Override
	public List<ExpenseAccountTemplateDTO> exportExpense(HttpServletResponse response, ExpenseAccountQueryVO dto) {
		List<ExpenseAccountTemplateDTO> list = Lists.newArrayList();
		if (CollectionUtil.isNotEmpty(dto.getIds())) {
			// 通过ids 查询商品信息
			Criteria criteria = Criteria.forClass(ExpenseAccountModel.class);
			Restriction r1 = Restrictions.in("id", dto.getIds().toArray());
			criteria.setRestriction(Restrictions.and(r1));
			List<ExpenseAccountModel> 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(ExpenseAccountModel.class);
		Restriction r1 = Restrictions.like("expenseCode", "%" + dto.getExpenseCode() + "%");
		Restriction r2 = Restrictions.like("expenseName", "%" + dto.getExpenseName() + "%");
		criteria.setRestriction(Restrictions.and(r1, r2));
		List<ExpenseAccountModel> 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<ExpenseAccountImportVO> importVOClass = ExpenseAccountImportVO.class;
			List<ExpenseAccountImportVO> 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_ACCOUNT_IMPORT, logAddVO);
			e.printStackTrace();
			throw new ParameterException(e.getMessage());
		}

	}

	private int importProcess(MultipartFile file, String data, UserEntity user) {
		Date date = new Date();
		// 商品资料对象集合
		List<ExpenseAccountModel> modelList = Lists.newArrayList();
		Properties properties = new Properties();
		// 待转换的对象
		Class<ExpenseAccountImportVO> importVOClass = ExpenseAccountImportVO.class;
		// 标准模板查询
		RestMessage<List<StandardTemplateDetailsListDTO>> restMessage = maindataTmsResource.listStandardTemplateDetailsExecute(BmsConstant.EXPENSE_ACCOUNT_IMPORT);
		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<ExpenseAccountImportVO> accountImportVOS;
		try {
			accountImportVOS = ExcelUtils.read(file, importVOClass);
		} catch (Exception e) {
			e.printStackTrace();
			throw new BizException("EXCEL转对象失败:" + e.getMessage());
		}

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

		int i = 1;
		for (ExpenseAccountImportVO itemImportStdVO : accountImportVOS) {
			if (ToolKits.isEmpty(itemImportStdVO.getExpenseCode())) {
				throw new PrologException("第" + i + "行导入数据,费用编码不能为空");
			}
			if (ToolKits.isEmpty(itemImportStdVO.getExpenseName())) {
				throw new PrologException("第" + i + "行导入数据,费用名称不能为空");
			}
			if (ToolKits.isEmpty(itemImportStdVO.getExpenseClassify())) {
				throw new PrologException("第" + i + "行导入数据,费用分类不能为空");
			}
			i++;
		}
		// 验证导入文件中费用编号是否重复
		Map<String, Long> codeMap = accountImportVOS.stream().collect(Collectors.groupingBy(ExpenseAccountImportVO::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 = accountImportVOS.stream().collect(Collectors.groupingBy(ExpenseAccountImportVO::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 (ExpenseAccountImportVO itemImportStdVO : accountImportVOS) {
			// 费用科目
			ExpenseAccountModel expenseAccountModel = new ExpenseAccountModel();
			BeanUtils.copyProperties(itemImportStdVO, expenseAccountModel);
			expenseAccountModel.setCreatetime(date);
			expenseAccountModel.setCreateperson(user.getNickname());
			expenseAccountModel.setUpdateperson(user.getNickname());
			expenseAccountModel.setUpdatetime(date);
			expenseAccountModel.setEnterpriseId(user.getEnterpriseId());
			expenseAccountModel.setEnterpriseName(user.getEnterpriseName());
			expenseAccountModel.setExpenseClassify(Integer.parseInt(itemImportStdVO.getExpenseClassify()));
			expenseAccountModel.setStatus(1);
			mapper.save(expenseAccountModel);

			modelList.add(expenseAccountModel);

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

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