
package org.sky.scientific.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.sky.core.log.exception.ServiceException;
import org.sky.core.mp.base.BaseServiceImpl;
import org.sky.core.mp.support.Condition;
import org.sky.core.mp.support.Query;
import org.sky.core.secure.utils.SecureUtil;
import org.sky.core.tool.api.R;
import org.sky.core.tool.utils.Charsets;
import org.sky.core.tool.utils.DateUtil;
import org.sky.core.tool.utils.Func;
import org.sky.core.tool.utils.StringUtil;
import org.sky.scientific.e.XmStatusEnum;
import org.sky.scientific.mapper.DepartmentMapper;
import org.sky.scientific.mapper.TechnicianMapper;
import org.sky.scientific.mapper.XmCostDetailsMapper;
import org.sky.scientific.mapper.XmMapper;
import org.sky.scientific.pojo.dto.XmDTO;
import org.sky.scientific.pojo.dto.XmUpdateDto;
import org.sky.scientific.pojo.entity.XmEntity;
import org.sky.scientific.pojo.vo.XmSummary;
import org.sky.scientific.pojo.vo.XmVO;
import org.sky.scientific.pojo.vo.XmYszbVO;
import org.sky.scientific.service.IXmService;
import org.sky.scientific.utils.TemplateService;
import org.sky.scientific.utils.ValidationUtils;
import org.sky.system.feign.ISysClient;
import org.sky.system.pojo.entity.Tenant;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 科研项目 服务实现类
 *
 * @author Kd
 * @since 2025-06-29
 */
@Service
@AllArgsConstructor
public class XmServiceImpl extends BaseServiceImpl<XmMapper, XmEntity> implements IXmService {

	private final TechnicianMapper technicianMapper;
	private final DepartmentMapper departmentMapper;
	private final XmCostDetailsMapper xmCostDetailsMapper;
	private final ISysClient sysClient;
	private final TemplateService templateService;

	@Override
	public IPage<XmEntity> selectXmList(IPage<XmEntity> page, XmDTO xm) {
		if (StringUtil.isBlank(xm.getYearAndMonth())) {
			xm.setYearAndMonth(DateUtil.format(new Date(), DateUtil.PATTERN_YYYYMM));
		}
		return page.setRecords(baseMapper.selectXmPage(page, xm));
	}

	@Override
	public List<XmEntity> selectXmListByYear(String year) {
		if (Func.isBlank(year)) {
			throw new ServiceException("参数year不能为空");
		}
		return baseMapper.selectXmListByYear(year);
	}

	/**
	 * 判断项目是否已结题
	 *
	 * @param xmId
	 */
	public boolean judgeXmIsOver(Long xmId, String dateStr, boolean isThrowEx) {
		JSONObject byXmId = baseMapper.getXmStatusByXmId(xmId);
		if (Func.isNull(byXmId)) {
			return false;
		}
		Date selDate = null;
		if (dateStr == null) {
			selDate = new Date();
		} else {
			//年月日
			if (dateStr.length() == 8) {
				selDate = DateUtil.parse(dateStr, DateUtil.PATTERN_YYYYMMDD);
			} else if (dateStr.length() == 6) {
				selDate = DateUtil.parse(dateStr, DateUtil.PATTERN_YYYYMM);
			} else if (dateStr.length() == 10 && dateStr.split("-").length == 3) {
				selDate = DateUtil.parse(dateStr, DateUtil.PATTERN_YYYY_MM_DD);
			}
		}
		Integer status = byXmId.getInteger("status");
		Date xmjssj = byXmId.getDate("xmjssj");
		if (Func.equals(status, XmStatusEnum.已结题.getCode()) || xmjssj.compareTo(selDate) < 0) {
			if (isThrowEx) {
				String xmbh = byXmId.getString("xmbh");
				throw new ServiceException("项目【" + xmbh + "】已结题/已结束，不可操作");
			} else {
				return true;
			}
		}
		return false;
	}

	private void judgeXmmcAndXmbhUnique(XmDTO xm) {
		//检查项目名称是否重复(xm);
		if (Func.isNotBlank(xm.getXmmc())) {
			Long xmIdByXmmc = baseMapper.getXmIdByXmmcOrXmbh(null, xm.getXmmc(), null);
			if (xmIdByXmmc != null && !xmIdByXmmc.equals(xm.getId())) {
				throw new ServiceException("研发项目名称已存在");
			}
		}

		if (Func.isNotBlank(xm.getXmbh())) {
			Long xmIdByXmbh = baseMapper.getXmIdByXmmcOrXmbh(null, null, xm.getXmbh());
			if (xmIdByXmbh != null && !xmIdByXmbh.equals(xm.getId())) {
				throw new ServiceException("研发项目编号已存在");
			}
		}
	}

	@Override
	public Long approval(XmDTO xm) {
		//判断项目名称是否有效
		this.judgeXmmcAvailable(xm.getXmmc());
		//判断项目名称和编号是否已存在
		judgeXmmcAndXmbhUnique(xm);
		if (Func.equals(xm.getStatus(), XmStatusEnum.暂存.getCode())) {
			if (Func.isBlank(xm.getXmbh())) {
				throw new ServiceException("请填写项目编号");
			}
		} else {
			List<String> validate = ValidationUtils.validate(xm);
			if (Func.isNotEmpty(validate)) {
				throw new ServiceException(String.join("\n", validate));
			}
			if (xm.getXmyszemx() != null) {
				List<JSONObject> list = JSONObject.parseArray(xm.getXmyszemx(), JSONObject.class);
				BigDecimal total = list.stream().map(item -> item.getBigDecimal("amount")).reduce(BigDecimal.valueOf(0.00), BigDecimal::add).setScale(2, RoundingMode.UP);
				if (total.compareTo(BigDecimal.ZERO) <= 0) {
					throw new ServiceException("请填写项目预算");
				}
				if (total.compareTo(xm.getXmysze()) != 0) {
					throw new ServiceException("项目预算总额明细不对");
				}
			} else {
				throw new ServiceException("请填写项目预算");
			}
		}
		return this.saveOrUpdate(xm) ? xm.getId() : null;
	}

	@Override
	public XmVO selectApprovalInfo(Long xmId) {
		XmEntity xm = baseMapper.selectById(xmId);
		if (Func.isNull(xm)) {
			throw new ServiceException("项目不存在");
		}
		xm.setStatus(baseMapper.getXmStatusByXmId(xmId).getInteger("status"));
		if (xm.getXmfzr() != null) {
			xm.setXmfzrxm(Func.toStr(technicianMapper.getNameById(xm.getXmfzr()), ""));
		} else {
			xm.setXmfzrxm("");
		}
		if (xm.getXmsqdwid() != null) {
			xm.setXmsqdw(Func.toStr(departmentMapper.getFullNameById(xm.getXmsqdwid()), ""));
		} else {
			xm.setXmsqdw("");
		}
		return cn.hutool.core.bean.BeanUtil.copyProperties(xm, XmVO.class);
	}

	@SneakyThrows
	@Override
	public void exportApprovalInfo(OutputStream os, Long xmId) {
		if (Func.isNull(xmId)) {
			return;
		}
		XmVO xmVO = this.selectApprovalInfo(xmId);

		JSONObject obj = new JSONObject();
		cn.hutool.core.bean.BeanUtil.copyProperties(xmVO, obj);
		obj.put("kssjStr", DateUtil.format(xmVO.getXmkssj(), "yyyy年MM月dd日"));
		obj.put("jssjStr", DateUtil.format(xmVO.getXmjssj(), "yyyy年MM月dd日"));

		StringBuilder sb = new StringBuilder();
		for (JSONObject json : JSON.parseArray(xmVO.getXmyszemx(), JSONObject.class)) {
			Integer year = json.getInteger("year");
			String amount = json.getString("amount");
			sb.append(year).append("年,").append(amount).append("万元");
			sb.append("\n");
		}
		obj.put("xmyszemxStr", sb.toString());

		obj.put("enterpriseName", "");
		R<Tenant> tenant = sysClient.getTenant(SecureUtil.getTenantId());
		if (tenant.isSuccess()) {
			obj.put("enterpriseName", tenant.getData().getTenantName());
		}

		templateService.processTemplate("项目立项表.ftl", obj, new OutputStreamWriter(os));
	}

	@Override
	public boolean updateXm(XmUpdateDto dto) {
		XmVO dbXm = baseMapper.selectByXmId(dto.getXmId());
//		if (XmStatusEnum.暂存.getCode() != dbXm.getStatus()) {
//			throw new ServiceException("只有项目状态为暂存时才能修改项目信息");
//		}
		return baseMapper.updateXm(dto);
	}

	@Override
	public IPage<XmSummary> summaryPage(IPage<XmSummary> page, XmDTO xm) {
		xm.setTenantId(SecureUtil.getTenantId());
		List<XmSummary> xmList = baseMapper.summaryPage(page, xm);

		int year = Integer.parseInt(xm.getYearAndMonth().substring(0, 4));

		//本年度已归集研发费用（万元）
		List<JSONObject> costByDateList = xmCostDetailsMapper.statisticsCost(xm.getYearAndMonth(), SecureUtil.getTenantId());
		//全周期已归集（万元）
		List<JSONObject> costAllList = xmCostDetailsMapper.statisticsCost(null, SecureUtil.getTenantId());

		for (XmSummary xmVO : xmList) {

			//立项报告数量
			xmVO.setLxbgCount(JSON.parseArray(xmVO.getLxbg()).size());

			List<JSONObject> xmysmx = JSON.parseArray(xmVO.getXmyszemx(), JSONObject.class);
			//预算总额
			xmVO.setXmysze(xmysmx.stream().map(item -> item.getBigDecimal("amount")).reduce(BigDecimal.valueOf(0.00), BigDecimal::add).setScale(2, RoundingMode.UP));

			//本年度预算
			for (JSONObject o : xmysmx) {
				if (o.getInteger("year") == year) {
					xmVO.setXmyszeCurrentYear(o.getBigDecimal("amount").setScale(2, RoundingMode.UP));
					break;
				}
			}

			//全周期已归集（万元）
			double qzqygj = costAllList.stream().filter(item -> Func.equals(item.getLong("xmId"), xmVO.getId())).mapToDouble(item -> item.getDouble("amountTotal")).sum();
			//元->万元
			xmVO.setQzqygj(BigDecimal.valueOf(qzqygj).multiply(new BigDecimal("0.0001")).setScale(2, RoundingMode.HALF_UP));
			//总预算完成率（%）
			if (xmVO.getQzqygj() == null || xmVO.getQzqygj().compareTo(BigDecimal.ZERO) <= 0 || xmVO.getXmysze() == null || xmVO.getXmysze().compareTo(BigDecimal.ZERO) <= 0) {
				xmVO.setZyswcl(BigDecimal.valueOf(0.00));
			} else {
				xmVO.setZyswcl(xmVO.getQzqygj().multiply(BigDecimal.valueOf(100)).divide(xmVO.getXmysze(), 2, RoundingMode.HALF_UP));
			}

			//本年度已归集研发费用（万元）
			double ndygjyffy = costByDateList.stream().filter(item -> Func.equals(item.getLong("xmId"), xmVO.getId())).mapToDouble(item -> item.getDouble("amountTotal")).sum();
			//元->万元
			xmVO.setNdygjyffy(BigDecimal.valueOf(ndygjyffy).multiply(new BigDecimal("0.0001")).setScale(2, RoundingMode.HALF_UP));
			//年度已归集金额/年度预算（%）xmyszeCurrentYear
			if (xmVO.getNdygjyffy() == null || xmVO.getNdygjyffy().compareTo(BigDecimal.ZERO) <= 0 || xmVO.getXmyszeCurrentYear() == null || xmVO.getXmyszeCurrentYear().compareTo(BigDecimal.ZERO) <= 0) {
				xmVO.setNdygjjel(BigDecimal.ZERO);
			} else {
				xmVO.setNdygjjel(xmVO.getNdygjyffy().divide(xmVO.getXmyszeCurrentYear(), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
			}
		}
		return page.setRecords(xmList);
	}

	@SneakyThrows
	@Override
	public void exportSummary(OutputStream os, XmDTO dto) {
		try {
			InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/研发项目管理/研发项目进度总览.xls");


			Query query = new Query().setCurrent(1).setSize(Integer.MAX_VALUE);

			List<XmSummary> list = this.summaryPage(Condition.getPage(query), dto).getRecords();
			AtomicInteger index = new AtomicInteger();
			for (XmSummary summary : list) {
				summary.setXh(index.incrementAndGet() + "");
				Optional<XmStatusEnum> first = Arrays.stream(XmStatusEnum.values()).filter(status -> status.getCode() == summary.getStatus()).findFirst();
				summary.setStatusName(first.isPresent() ? first.get().getName() : "");

				summary.setXmkssjStr(Func.notNull(summary.getXmkssj()) ? DateUtil.format(summary.getXmkssj(), DateUtil.PATTERN_DATE) : null);
				summary.setXmjssjStr(Func.notNull(summary.getXmjssj()) ? DateUtil.format(summary.getXmjssj(), DateUtil.PATTERN_DATE) : null);

				summary.setLxbgStatus((summary.getLxbgCount() > 0) ? "已提交" : "未提交");
				summary.setJdxbgStatus((summary.getJdxbgCount() > 0) ? "已提交" : "未提交");
				summary.setYsbgStatus((summary.getYsbgCount() > 0) ? "已提交" : "未提交");
			}
			ExcelWriter excelWriter = EasyExcel.write(os)
				.withTemplate((templateFileName))
				.excelType(ExcelTypeEnum.XLS)
				.build();

			WriteSheet writeSheet = EasyExcel.writerSheet().build();
			//设置list集合数据
			excelWriter.fill(list, writeSheet);
			Map<String, Object> map = new HashMap<>();
			map.put("year", dto.getYearAndMonth().substring(0, 4));
			map.put("month", dto.getYearAndMonth().substring(4));
			excelWriter.fill(map, writeSheet);
			excelWriter.finish();
			if (templateFileName != null) {
				templateFileName.close();
			}
		} catch (Exception e) {
			throw new ServiceException("导出失败，" + e.getLocalizedMessage());
		}
	}

	@Override
	public XmYszbVO selectYszb(Long xmId) {
		if (xmId == null || xmId <= 0) {
			throw new ServiceException("请选择研发项目");
		}
		XmYszbVO vo = baseMapper.selectYszb(xmId);

		vo.setTotal(vo.getRyrgfy().add(vo.getZjtrfy()).add(vo.getZjfyycqdtfy()).add(vo.getWxzctxfy()).add(vo.getSjfy()).add(vo.getZbtsysyfy()).add(vo.getWtwbyjkffy()).add(vo.getQtfy()));
		if (vo.getTotal().compareTo(BigDecimal.ZERO) <= 0) {
			vo.setRyrgfyRate(0.00);
			vo.setZjtrfyRate(0.00);
			vo.setZjfyycqdtfyRate(0.00);
			vo.setWxzctxfyRate(0.00);
			vo.setSjfyRate(0.00);
			vo.setZbtsysyfyRate(0.00);
			vo.setWtwbyjkffyRate(0.00);
			vo.setQtfyRate(0.00);
		} else {
			vo.setRyrgfyRate(vo.getRyrgfy().divide(vo.getTotal(), 2, RoundingMode.HALF_UP).doubleValue());
			vo.setZjtrfyRate(vo.getZjtrfy().divide(vo.getTotal(), 2, RoundingMode.HALF_UP).doubleValue());
			vo.setZjfyycqdtfyRate(vo.getZjfyycqdtfy().divide(vo.getTotal(), 2, RoundingMode.HALF_UP).doubleValue());
			vo.setWxzctxfyRate(vo.getWxzctxfy().divide(vo.getTotal(), 2, RoundingMode.HALF_UP).doubleValue());
			vo.setSjfyRate(vo.getSjfy().divide(vo.getTotal(), 2, RoundingMode.HALF_UP).doubleValue());
			vo.setZbtsysyfyRate(vo.getZbtsysyfy().divide(vo.getTotal(), 2, RoundingMode.HALF_UP).doubleValue());
			vo.setWtwbyjkffyRate(vo.getWtwbyjkffy().divide(vo.getTotal(), 2, RoundingMode.HALF_UP).doubleValue());
			vo.setQtfyRate(vo.getQtfy().divide(vo.getTotal(), 2, RoundingMode.HALF_UP).doubleValue());
		}

		BigDecimal totalRate = BigDecimal.ZERO;
		if (StringUtil.isNotBlank(vo.getXmyszemx())) {
			BigDecimal temp = JSONObject.parseArray(vo.getXmyszemx(), JSONObject.class).stream().map(item -> item.getBigDecimal("amount")).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(4, RoundingMode.UP);
			totalRate = vo.getTotal().divide(BigDecimal.valueOf(10000L)).divide(temp, 4, RoundingMode.UP).multiply(BigDecimal.valueOf(100));
		}
		vo.setTotalRate(Math.min(totalRate.doubleValue(), 100.0));
		vo.setXmsqdw(departmentMapper.getFullNameById(vo.getXmsqdwid()));
		return vo;
	}

	@SneakyThrows
	@Override
	public void exportYszb(OutputStream os, Long xmId) {
		XmYszbVO vo = this.selectYszb(xmId);

		vo.setRyrgfy(vo.getRyrgfy().divide(BigDecimal.valueOf(10000L), 4, RoundingMode.UP));
		vo.setZjtrfy(vo.getZjtrfy().divide(BigDecimal.valueOf(10000L), 4, RoundingMode.UP));
		vo.setZjfyycqdtfy(vo.getZjfyycqdtfy().divide(BigDecimal.valueOf(10000L), 4, RoundingMode.UP));
		vo.setWxzctxfy(vo.getWxzctxfy().divide(BigDecimal.valueOf(10000L), 4, RoundingMode.UP));
		vo.setSjfy(vo.getSjfy().divide(BigDecimal.valueOf(10000L), 4, RoundingMode.UP));
		vo.setZbtsysyfy(vo.getZbtsysyfy().divide(BigDecimal.valueOf(10000L), 4, RoundingMode.UP));
		vo.setWtwbyjkffy(vo.getWtwbyjkffy().divide(BigDecimal.valueOf(10000L), 4, RoundingMode.UP));
		vo.setQtfy(vo.getQtfy().divide(BigDecimal.valueOf(10000L), 4, RoundingMode.UP));
		vo.setTotal(vo.getTotal().divide(BigDecimal.valueOf(10000L), 4, RoundingMode.UP));

		vo.setRyrgfyRate(vo.getRyrgfyRate() * 100.00);
		vo.setZjtrfyRate(vo.getZjtrfyRate() * 100.00);
		vo.setZjfyycqdtfyRate(vo.getZjfyycqdtfyRate() * 100.00);
		vo.setWxzctxfyRate(vo.getWxzctxfyRate() * 100.00);
		vo.setSjfyRate(vo.getSjfyRate() * 100.00);
		vo.setZbtsysyfyRate(vo.getZbtsysyfyRate() * 100.00);
		vo.setWtwbyjkffyRate(vo.getWtwbyjkffyRate() * 100.00);
		vo.setQtfyRate(vo.getQtfyRate() * 100.00);

		vo.setXmkssjStr(Func.notNull(vo.getXmkssj()) ? DateUtil.format(vo.getXmkssj(), "yyyy年MM月dd日") : null);
		vo.setXmjssjStr(Func.notNull(vo.getXmjssj()) ? DateUtil.format(vo.getXmjssj(), "yyyy年MM月dd日") : null);

		InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/研发项目管理/预算编制/研发项目经费预算表.xls");
		ExcelWriter excelWriter = EasyExcel.write(os)
			.withTemplate((templateFileName))
			.excelType(ExcelTypeEnum.XLS)
			.build();

		WriteSheet writeSheet = EasyExcel.writerSheet().build();
		excelWriter.fill(vo, writeSheet);
		excelWriter.finish();
		if (templateFileName != null) {
			templateFileName.close();
		}
	}

	@Override
	public XmVO selectByXmId(Long xmId) {
		XmVO xmVO = baseMapper.selectByXmId(xmId);
		xmVO.setXmfzrxm(technicianMapper.getNameById(xmVO.getXmfzr()));
		xmVO.setXmsqdw(departmentMapper.getFullNameById(xmVO.getXmsqdwid()));
		return xmVO;
	}

	@Override
	public IPage<JSONObject> selectAssistList(IPage<JSONObject> page, XmDTO xm) {
		if (StringUtil.isBlank(xm.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}

		return page.setRecords(baseMapper.selectAssistList(page, xm));
	}

	@SneakyThrows
	@Override
	public void exportAssistXmList(HttpServletResponse response, XmDTO dto) {
		List<JSONObject> list = this.selectAssistList(Condition.getPage(new Query().setCurrent(1).setSize(Integer.MAX_VALUE)), dto).getRecords();
		AtomicInteger index = new AtomicInteger(1);

		list.forEach(temp -> {
			temp.put("xh", index.getAndIncrement() + "");
		});

		InputStream templateFileName = this.getClass().getClassLoader().getResourceAsStream("export-template/基础资源管理/辅助核算项目.xls");
		response.setContentType("application/vnd.ms-excel");
		response.setCharacterEncoding("utf-8");
		response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("辅助核算项目.xls", Charsets.UTF_8));
		ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
			.withTemplate((templateFileName))
			.excelType(ExcelTypeEnum.XLS)
			.build();

		WriteSheet writeSheet = EasyExcel.writerSheet().build();
		FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
		//设置list集合数据
		excelWriter.fill(list, fillConfig, writeSheet);
		Map<String, Object> map = new HashMap<>();
		map.put("year", dto.getYearAndMonth());
		excelWriter.fill(map, writeSheet);
		excelWriter.finish();
		if (templateFileName != null) {
			templateFileName.close();
		}
	}


	/**
	 * 判断项目名称是否有效
	 *
	 * @param xmmc
	 */
	private void judgeXmmcAvailable(String xmmc) {
		//技改、技术改造、推广、打样、翻新、产业化、生产线、改造、年产、示范
		List<String> errorWords = Arrays.asList("技改", "技术改造", "推广", "打样", "翻新", "产业化", "生产线", "改造", "年产", "示范");
		//xmmc包含errorWords，则报错
		for (String errorWord : errorWords) {
			if (xmmc.contains(errorWord)) {
				throw new ServiceException("研发项目名称，不符合统计部门的命名要求！");
			}
		}
	}

}
