
package org.sky.scientific.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.sky.core.log.exception.ServiceException;
import org.sky.core.mp.base.BaseServiceImpl;
import org.sky.core.secure.utils.SecureUtil;
import org.sky.core.tool.utils.CollectionUtil;
import org.sky.core.tool.utils.Func;
import org.sky.core.tool.utils.StringUtil;
import org.sky.scientific.excel.XmAssetHoursExcel;
import org.sky.scientific.excel.XmAssetHoursListener;
import org.sky.scientific.excel.handle.AssetWorkMergeStrategy;
import org.sky.scientific.excel.handle.CustomCellStyleHandler;
import org.sky.scientific.mapper.AssetMapper;
import org.sky.scientific.mapper.XmAssetHoursMapper;
import org.sky.scientific.mapper.XmMapper;
import org.sky.scientific.pojo.dto.XmAssetHoursDto;
import org.sky.scientific.pojo.entity.AssetEntity;
import org.sky.scientific.pojo.entity.XmAssetHoursEntity;
import org.sky.scientific.pojo.vo.DailyAttendance;
import org.sky.scientific.pojo.vo.XmAssetHoursVO;
import org.sky.scientific.service.IXmAssetHoursService;
import org.sky.scientific.service.IXmService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.OutputStream;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 科研仪器设备工时 服务实现类
 *
 * @author Kd
 * @since 2025-06-29
 */
@Slf4j
@Service
@AllArgsConstructor
public class XmAssetHoursServiceImpl extends BaseServiceImpl<XmAssetHoursMapper, XmAssetHoursEntity> implements IXmAssetHoursService {

	private final XmMapper xmMapper;
	private final AssetMapper assetMapper;
	private final IXmService xmService;

	@SneakyThrows
	@Transactional(rollbackFor = Exception.class)
	@Override
	public void importXmAssetHours(MultipartFile file, String yearAndMonth) {
		XmAssetHoursListener listener = new XmAssetHoursListener(yearAndMonth);
		EasyExcel.read(new BufferedInputStream(file.getInputStream()))
			.registerReadListener(listener)
			.sheet(0)
			.headRowNumber(3) // 重要：设置表头行数(0-based)
			.doRead();
		List<XmAssetHoursExcel> records = listener.getRecords();
		if (CollectionUtil.isEmpty(records)) {
			throw new ServiceException("未能从模板中读取数据，请检查模板格式是否正确");
		}

		//需要新增的，以便后续批量新增
		List<XmAssetHoursEntity> insertList = new ArrayList<>();
		//需要更新的，以便后续批量修改
		List<XmAssetHoursEntity> updateList = new ArrayList<>();

		int batchCount = 20;

		List<XmAssetHoursEntity> dbList = baseMapper.selectByYearAndMonth(yearAndMonth, null, null);
		for (XmAssetHoursExcel record : records) {
			//保存
			if (StringUtil.isBlank(record.getXmbh())) {
				throw new ServiceException("研发项目编号为空，请检查数据");
			}
			Long xmId = xmMapper.getXmIdByXmmcOrXmbh(null, null, record.getXmbh());
			if (xmId == null) {
				throw new ServiceException("序号为[" + record.getSerialNumber() + "]的研发项目不存在");
			}

			if (StringUtil.isBlank(record.getZcbm())) {
				throw new ServiceException("序号为[" + record.getSerialNumber() + "]的资产编码为空");
			}
			AssetEntity dbAsset = assetMapper.selectOne(Wrappers.<AssetEntity>lambdaQuery().eq(AssetEntity::getZcbm, record.getZcbm()).eq(AssetEntity::getYearAndMonth, yearAndMonth));
			if (null == dbAsset) {
				throw new ServiceException("序号为[" + record.getSerialNumber() + "]的资产不存在");
			}
			//保存总工时
			updateZgs(dbAsset.getId(), record.getZgs(), yearAndMonth);

			List<XmAssetHoursEntity> byXmAndAssetList = dbList.stream().filter(e -> e.getXmId().equals(xmId) && e.getAssetId().equals(dbAsset.getId())).toList();

			List<XmAssetHoursEntity> dbAssetHours = baseMapper.selectByYearAndMonth(yearAndMonth, null, dbAsset.getId());
			for (DailyAttendance dailyAttendance : record.getDailyAttendances()) {

				xmService.judgeXmIsOver(xmId, dailyAttendance.getWorkDate(), true);

				Map<String, List<XmAssetHoursEntity>> mapByDate = dbAssetHours.stream().collect(Collectors.groupingBy(XmAssetHoursEntity::getWorkDate));
				List<XmAssetHoursEntity> dbHourList = mapByDate.get(dailyAttendance.getWorkDate());
				if(Func.isEmpty(dbHourList) ){
					dbHourList = new ArrayList<>();
					mapByDate.put(dailyAttendance.getWorkDate(), dbHourList);
				}
				for (XmAssetHoursEntity hours : dbHourList) {
					if(Func.equals(hours.getWorkDate(), dailyAttendance.getWorkDate()) && Func.equals(hours.getXmId(), xmId)){
						hours.setWorkHours(dailyAttendance.getWorkHours());
						break;
					}
				}
				double totalHoursByDay = dbHourList.stream().mapToDouble(XmAssetHoursEntity::getWorkHours).sum();
				if(totalHoursByDay > 24){
					throw new ServiceException("设备在" + dailyAttendance.getWorkDate() +"这天的工时超过了24小时，请核对数据");
				}

				List<Long> primaryKeyList = byXmAndAssetList.stream().filter(e -> Objects.equals(e.getWorkDate(), dailyAttendance.getWorkDate())).map(XmAssetHoursEntity::getId).toList();
				Long primaryKey = CollectionUtil.isEmpty(primaryKeyList) ? null : primaryKeyList.get(0);
				XmAssetHoursEntity entity = new XmAssetHoursEntity();
				if (primaryKey == null) {
					entity.setXmId(xmId);
					entity.setAssetId(dbAsset.getId());
					entity.setWorkDate(dailyAttendance.getWorkDate());
					entity.setWorkHours(dailyAttendance.getWorkHours());
					entity.setTenantId(SecureUtil.getTenantId());
					entity.setCreateUser(SecureUtil.getUserId());
					entity.setCreateDept(Func.toLong(SecureUtil.getDeptId()));
					insertList.add(entity);
				} else {
					entity.setId(primaryKey);
					entity.setWorkHours(dailyAttendance.getWorkHours());
					updateList.add(entity);
				}
				if (!insertList.isEmpty() && insertList.size() % batchCount == 0) {
					int insertCount = baseMapper.batchInsert(insertList);
					if (insertCount == batchCount) {
						insertList.clear();
					}

				}
				if (!updateList.isEmpty() && updateList.size() % batchCount == 0) {
					int updateCount = baseMapper.batchUpdate(updateList);
					if (updateCount == batchCount) {
						updateList.clear();
					}
				}
			}
		}
		if (!insertList.isEmpty()) {
			baseMapper.batchInsert(insertList);
		}
		if (!updateList.isEmpty()) {
			baseMapper.batchUpdate(updateList);
		}
	}

	@Override
	public IPage<XmAssetHoursVO> selectXmAssetHoursPage(XmAssetHoursDto dto, IPage<XmAssetHoursVO> page) {
		if (StringUtil.isBlank(dto.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}
		List<XmAssetHoursVO> list = baseMapper.selectXmAssetHoursPage(page, dto);
		for (XmAssetHoursVO temp : list) {
			List<DailyAttendance> dailyAttendanceList = baseMapper.selectHoursByXmIdAndAssetId(temp.getXmId(), temp.getAssetId(), dto.getYearAndMonth());
			if (CollectionUtil.isEmpty(dailyAttendanceList)) {
				continue;
			}
			temp.setDailyAttendances(dailyAttendanceList);
		}
		return page.setRecords(list);
	}

	private void updateZgs(Long assetId, Double zgs, String yearAndMonth) {
		if (zgs != null) {
			//不能大于当月工作天数（自然天数）X24小时
			// 解析年份和月份
			int year = Integer.parseInt(yearAndMonth.substring(0, 4));
			int month = Integer.parseInt(yearAndMonth.substring(4));

			// 获取YearMonth对象并得到天数
			YearMonth yearMonth = YearMonth.of(year, month);
			int days = yearMonth.lengthOfMonth();
			if (zgs > days * 24) {
				throw new ServiceException("总工时不能大于当月工作天数X24小时");
			}
			AssetEntity asset = new AssetEntity();
			asset.setId(assetId);
			asset.setZgs(zgs);
			assetMapper.updateById(asset);
		}
	}

	@Override
	@Transactional
	public boolean batchSubmitXmAssetHours(XmAssetHoursDto dto) {
		if (StringUtil.isBlank(dto.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}
		if (dto.getXmId() == null) {
			throw new ServiceException("参数xmId不能为空");
		}

		if (dto.getAssetId() == null) {
			throw new ServiceException("参数assetId不能为空");
		}

		AssetEntity dbAsset = assetMapper.selectById(dto.getAssetId());
		if (Func.isEmpty(dbAsset)) {
			throw new ServiceException("资产不存在");
		}

		updateZgs(dbAsset.getId(), dto.getZgs(), dto.getYearAndMonth());

		List<XmAssetHoursEntity> dbList = baseMapper.selectByYearAndMonth(dto.getYearAndMonth(), dto.getXmId(), dto.getAssetId());

		//需要新增的，以便后续批量新增
		List<XmAssetHoursEntity> insertList = new ArrayList<>();
		//需要更新的，以便后续批量修改
		List<XmAssetHoursEntity> updateList = new ArrayList<>();

		List<XmAssetHoursEntity> dbAssetHours = baseMapper.selectByYearAndMonth(dto.getYearAndMonth(), null, dto.getAssetId());

		for (DailyAttendance dailyAttendance : dto.getDailyHoursList()) {

			xmService.judgeXmIsOver(dto.getXmId(), dailyAttendance.getWorkDate(),true);

			Map<String, List<XmAssetHoursEntity>> mapByDate = dbAssetHours.stream().collect(Collectors.groupingBy(XmAssetHoursEntity::getWorkDate));
			List<XmAssetHoursEntity> dbHourList = mapByDate.get(dailyAttendance.getWorkDate());
			if(Func.isEmpty(dbHourList) ){
				dbHourList = new ArrayList<>();
				mapByDate.put(dailyAttendance.getWorkDate(), dbHourList);
			}
			for (XmAssetHoursEntity hours : dbHourList) {
				if(Func.equals(hours.getWorkDate(), dailyAttendance.getWorkDate()) && Func.equals(hours.getXmId(), dto.getXmId())){
					hours.setWorkHours(dailyAttendance.getWorkHours());
					break;
				}
			}
			double totalHoursByDay = dbHourList.stream().mapToDouble(XmAssetHoursEntity::getWorkHours).sum();
			if(totalHoursByDay > 24){
				throw new ServiceException("设备在" + dailyAttendance.getWorkDate() +"这天的工时超过了24小时，请核对数据");
			}
			List<Long> primaryKeyList = dbList.stream().filter(e -> Objects.equals(e.getWorkDate(), dailyAttendance.getWorkDate())).map(XmAssetHoursEntity::getId).toList();
			Long primaryKey = CollectionUtil.isEmpty(primaryKeyList) ? null : primaryKeyList.get(0);
			XmAssetHoursEntity entity = new XmAssetHoursEntity();
			if (primaryKey == null) {
				entity.setXmId(dto.getXmId());
				entity.setAssetId(dto.getAssetId());
				entity.setWorkDate(dailyAttendance.getWorkDate());
				entity.setWorkHours(dailyAttendance.getWorkHours());
				entity.setTenantId(SecureUtil.getTenantId());
				entity.setCreateUser(SecureUtil.getUserId());
				entity.setCreateDept(Func.toLong(SecureUtil.getDeptId()));
				insertList.add(entity);
			} else {
				entity.setId(primaryKey);
				entity.setWorkHours(dailyAttendance.getWorkHours());
				updateList.add(entity);
			}
		}

		if (!insertList.isEmpty()) {
			baseMapper.batchInsert(insertList);
			insertList.clear();
		}
		if (!updateList.isEmpty()) {
			baseMapper.batchUpdate(updateList);
			updateList.clear();
		}
		return true;
	}

	@Override
	public boolean clearXmAssetHours(JSONObject dto) {
		JSONArray xmAndAssetIdList = dto.getJSONArray("xmAndAssetIdList");
		if (xmAndAssetIdList == null || xmAndAssetIdList.isEmpty()) {
			throw new ServiceException("参数xmAndAssetIdList不能为空");
		}
		for (int i = 0; i < xmAndAssetIdList.size(); i++) {
			JSONObject object = xmAndAssetIdList.getJSONObject(i);
			Long xmId = object.getLong("xmId");

			Long assetId = object.getLong("assetId");
			String yearAndMonth = dto.getString("yearAndMonth");

			xmService.judgeXmIsOver(xmId, yearAndMonth,true);
			baseMapper.clearXmAssetHours(yearAndMonth, xmId, assetId);
		}
		return true;
	}

	@Override
	public void exportXmAssetHours(OutputStream os, List<XmAssetHoursVO> list, String yearAndMonth) {
		try {
			AtomicInteger index = new AtomicInteger(1);
			for (XmAssetHoursVO vo : list) {
				vo.setXh(index.getAndIncrement() + "");
			}
			Integer year = Integer.valueOf(yearAndMonth.substring(0, 4));
			Integer month = Integer.valueOf(yearAndMonth.substring(4));
			// 构建数据
			List<List<Object>> data = buildData(list, year, month);
			// 导出Excel（包含合并单元格策略）
			EasyExcel.write(os)
				.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
				.registerWriteHandler(new AssetWorkMergeStrategy(year, month))
				.registerWriteHandler(new CustomCellStyleHandler())
				.autoCloseStream(true)
				.sheet("每月仪器设备工时打卡")
				.doWrite(data);
		} catch (Exception e) {
			throw new ServiceException("导出失败：" + e.getLocalizedMessage());
		}
	}

	/**
	 * 构建数据行
	 */
	private List<List<Object>> buildData(List<XmAssetHoursVO> list, Integer year, Integer month) {
		List<List<Object>> data = new ArrayList<>();

		LocalDate startDate = LocalDate.of(year, month, 1);
		int daysInMonth = startDate.lengthOfMonth();
		DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
		DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("E", Locale.CHINA);
		DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("d");

		// 第一行：标题行
		List<Object> titleRow = new ArrayList<>();
		titleRow.add("每月仪器设备工时打卡" + year + "年" + month + "月");
		for (int i = 1; i < daysInMonth + 9; i++) { // 9 = 9个固定列
			titleRow.add("");
		}
		data.add(titleRow);

		// 第二行：纵向表头标题
		List<Object> verticalHeaderRow = new ArrayList<>();
		verticalHeaderRow.add("序号");
		verticalHeaderRow.add("研发项目编号");
		verticalHeaderRow.add("研发项目名称");
		verticalHeaderRow.add("资产编码");
		verticalHeaderRow.add("资产名称");
		verticalHeaderRow.add("资产类别");
		verticalHeaderRow.add("用途");
		verticalHeaderRow.add("总工时");
		verticalHeaderRow.add("使用工时");
		//第二行：周几行
		for (int i = 1; i <= daysInMonth; i++) {
			LocalDate date = LocalDate.of(year, month, i);
			verticalHeaderRow.add(date.format(weekFormatter).substring(1));
		}
		data.add(verticalHeaderRow);

		// 第三行：日期数字行
		List<Object> dayRow = new ArrayList<>();
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		dayRow.add("");
		for (int day = 1; day <= daysInMonth; day++) {
			LocalDate date = LocalDate.of(year, month, day);
			dayRow.add(date.format(dayFormatter));
		}
		data.add(dayRow);

		// 第4行开始：数据行
		for (XmAssetHoursVO vo : list) {
			List<Object> dataRow = new ArrayList<>();

			// 序号
			dataRow.add(vo.getXh());
			// 研发项目编号
			dataRow.add(vo.getXmbh());
			//研发项目名称
			dataRow.add(vo.getXmmc());
			// 资产编码
			dataRow.add(vo.getZcbm());
			//资产名称
			dataRow.add(vo.getZcmc());
			// 资产类别
			dataRow.add(vo.getZclb());
			// 用途
			dataRow.add(vo.getYt());
			//总工时
			dataRow.add(vo.getZgs());
			// 使用工时
			dataRow.add(vo.getMonthlyHours());

			// 每日工时数据
			Map<String, Double> dailyMap = new HashMap<>();

			for (DailyAttendance daily : vo.getDailyAttendances()) {
				dailyMap.put(daily.getWorkDate(), daily.getWorkHours());
			}

			for (int day = 1; day <= daysInMonth; day++) {
				LocalDate currentDate = LocalDate.of(year, month, day);
				String dateKey = currentDate.format(dateFormatter);
				dataRow.add(dailyMap.getOrDefault(dateKey, 0.0));
			}
			data.add(dataRow);
		}
		return data;
	}

}
