
package org.sky.scientific.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
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.utils.Charsets;
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.AttendanceMonthlyDataListener;
import org.sky.scientific.excel.AttendanceMonthlyExcel;
import org.sky.scientific.excel.handle.AttendanceMergeStrategy;
import org.sky.scientific.excel.handle.CustomCellStyleHandler;
import org.sky.scientific.mapper.AttendanceMapper;
import org.sky.scientific.mapper.SettingMapper;
import org.sky.scientific.mapper.TechnicianMapper;
import org.sky.scientific.pojo.dto.AttendanceDto;
import org.sky.scientific.pojo.entity.AttendanceEntity;
import org.sky.scientific.pojo.entity.SettingEntity;
import org.sky.scientific.pojo.vo.AttendanceVO;
import org.sky.scientific.pojo.vo.DailyAttendance;
import org.sky.scientific.service.IAttendanceService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 考勤管理 服务实现类
 *
 * @author Kd
 * @since 2025-06-29
 */
@Service
@AllArgsConstructor
public class AttendanceServiceImpl extends BaseServiceImpl<AttendanceMapper, AttendanceEntity> implements IAttendanceService {

	private final TechnicianMapper technicianMapper;
	private final SettingMapper settingMapper;

	@Override
	public IPage<AttendanceVO> selectAttendancePage(AttendanceDto attendance, IPage<AttendanceVO> page) {
		if (StringUtil.isBlank(attendance.getYearAndMonth())) {
			throw new ServiceException("参数yearAndMonth不能为空");
		}
		List<AttendanceVO> list = baseMapper.selectAttendancePage(page, attendance);
		for (AttendanceVO vo : list) {
			List<DailyAttendance> dailyAttendanceList = baseMapper.selectDailyAttendanceByUnicodeAndMonth(vo.getUnicode(), attendance.getYearAndMonth());
			if (CollectionUtil.isEmpty(dailyAttendanceList)) {
				continue;
			}
			vo.setMonthlyHours(dailyAttendanceList.stream().mapToDouble(DailyAttendance::getWorkHours).sum());

			SettingEntity one = settingMapper.selectOne(Wrappers.<SettingEntity>lambdaQuery().eq(SettingEntity::getYearAndMonth, attendance.getYearAndMonth().substring(0, 4)));
			if (Func.isNull(one) || Func.isNull(one.getWorkHours())) {
				throw new ServiceException("企业未设置考勤时长");
			}

			Double workHours = one.getWorkHours();
			vo.setMonthlyDays(vo.getMonthlyHours() / workHours);
			vo.setDailyAttendances(dailyAttendanceList);
		}
		return page.setRecords(list);
	}

	@Override
	public boolean submit(AttendanceEntity attendance) {
		int affectNum = 0;
		AttendanceEntity one = baseMapper.selectOne(Wrappers.<AttendanceEntity>lambdaQuery().
			eq(AttendanceEntity::getUnicode, attendance.getUnicode()).eq(AttendanceEntity::getWorkDate, attendance.getWorkDate()));
		if (one == null) {
			//新增
			affectNum = baseMapper.insert(attendance);
		} else {
			//修改
			LambdaQueryWrapper<AttendanceEntity> updateWrapper = Wrappers.<AttendanceEntity>lambdaQuery()
				.eq(AttendanceEntity::getUnicode, attendance.getUnicode())
				.eq(AttendanceEntity::getWorkDate, attendance.getWorkDate());
			affectNum = baseMapper.update(attendance, updateWrapper);
		}
		return affectNum == 1;
	}

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

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

		int batchCount = 20;

		List<AttendanceEntity> dbList = baseMapper.selectByYearAndMonth(yearAndMonth);
		for (AttendanceMonthlyExcel record : records) {
			//保存
			String unicode = technicianMapper.getUnicodeByNumberAndIdentityCard(record.getNumber(), record.getIdCard());
			if (unicode == null) {
				throw new ServiceException("序号为[" + record.getSerialNumber() + "]的工号和者身份证都不存在");
			}
			List<AttendanceEntity> dataByTechnicianMonthly = dbList.stream().filter(e -> e.getUnicode().equals(unicode)).toList();
			for (DailyAttendance dailyAttendance : record.getDailyAttendances()) {

				List<Long> primaryKeyList = dataByTechnicianMonthly.stream().filter(e -> Objects.equals(e.getWorkDate(), dailyAttendance.getWorkDate())).map(AttendanceEntity::getId).toList();
				Long primaryKey = CollectionUtil.isEmpty(primaryKeyList) ? null : primaryKeyList.get(0);
				AttendanceEntity entity = new AttendanceEntity();
				if (primaryKey == null) {
					entity.setUnicode(unicode);
					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 void exportAttendance(HttpServletResponse response, AttendanceDto dto) {

		try {
			response.setContentType("application/vnd.ms-excel");
			response.setCharacterEncoding("utf-8");
			response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("每月考勤信息.xls", Charsets.UTF_8));

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

			List<AttendanceVO> list = this.selectAttendancePage(dto, Condition.getPage(query)).getRecords();
			AtomicInteger index = new AtomicInteger(1);
			for (AttendanceVO vo : list) {
				vo.setXh(index.getAndIncrement());
			}
			Integer year = Integer.valueOf(dto.getYearAndMonth().substring(0, 4));
			Integer month = Integer.valueOf(dto.getYearAndMonth().substring(5));

			// 构建数据
			List<List<Object>> data = buildData(list, year, month);

			// 导出Excel（包含合并单元格策略）
			EasyExcel.write(response.getOutputStream())
				.registerWriteHandler(new AttendanceMergeStrategy(year, month))
				.registerWriteHandler(new CustomCellStyleHandler())
				.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
				.autoCloseStream(true)
				.sheet("考勤信息")
				.doWrite(data);

		} catch (Exception e) {
			throw new ServiceException("导出失败：" + e.getLocalizedMessage());
		}
	}

	/**
	 * 构建数据行
	 */
	private List<List<Object>> buildData(List<AttendanceVO> attendanceList, 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 + 7; i++) { // 7 = 5个固定列 + 2个统计列
			titleRow.add("");
		}
		data.add(titleRow);

		// 第二行：纵向表头标题
		List<Object> verticalHeaderRow = new ArrayList<>();
		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));
		}
 		verticalHeaderRow.add("本月出勤/小时");
		verticalHeaderRow.add("本月出勤/天");
		data.add(verticalHeaderRow);

		// 第三行：日期数字行
		List<Object> dayRow = new ArrayList<>();
		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));
		}
		dayRow.add("");
		dayRow.add("");
		data.add(dayRow);

		// 第4行开始：数据行
		for (AttendanceVO attendance : attendanceList) {
			List<Object> dataRow = new ArrayList<>();

			// 序号
			dataRow.add(attendance.getXh());
			// 姓名
			dataRow.add(attendance.getName());
			// 工号
			dataRow.add(attendance.getNumber());
			// 身份证号码
			dataRow.add(attendance.getIdCard());
			// 科研人员类别
			dataRow.add(attendance.getPersonnelType());

			// 每日考勤数据
			Map<String, Double> dailyMap = new HashMap<>();
			if (attendance.getDailyAttendances() != null) {
				for (DailyAttendance daily : attendance.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));
			}

			// 统计信息
			dataRow.add(attendance.getMonthlyHours());
			dataRow.add(attendance.getMonthlyDays());

			data.add(dataRow);
		}

		return data;
	}

	@Override
	public boolean batchSubmit(AttendanceDto attendance) {
		List<AttendanceEntity> dbList = baseMapper.selectByYearAndMonth(attendance.getYearAndMonth());
		List<AttendanceEntity> dataByTechnicianMonthly = dbList.stream().filter(e -> e.getUnicode().equals(attendance.getUnicode())).toList();

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

		for (DailyAttendance dailyAttendance : attendance.getDailyAttendances()) {

			List<Long> primaryKeyList = dataByTechnicianMonthly.stream().filter(e -> Objects.equals(e.getWorkDate(), dailyAttendance.getWorkDate())).map(AttendanceEntity::getId).toList();
			Long primaryKey = CollectionUtil.isEmpty(primaryKeyList) ? null : primaryKeyList.get(0);
			AttendanceEntity entity = new AttendanceEntity();
			if (primaryKey == null) {
				entity.setUnicode(attendance.getUnicode());
				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 clearAttendance(List<AttendanceDto> attendanceList) {
		if (CollectionUtil.isEmpty(attendanceList)) {
			throw new ServiceException("参数不能为空");
		}
		int affectNum = 0;
		for (AttendanceDto entity : attendanceList) {
			affectNum += baseMapper.clearUnicodeAndMonthly(entity.getUnicode(), entity.getYearAndMonth());
		}
		return affectNum >= 0;
	}
}
