/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.attence.record.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;

import javax.validation.Valid;

import org.checkerframework.checker.units.qual.A;
import org.springblade.attence.record.dto.QueryMarkDaysDTO;
import org.springblade.attence.record.vo.MarkDayVo;
import org.springblade.attence.record.vo.MyStatisticalVo;
import org.springblade.attence.record.vo.RecordAppVO;
import org.springblade.attence.rule.entity.Rule;
import org.springblade.attence.rule.service.IRuleService;
import org.springblade.attence.ruleperson.entity.RulePerson;
import org.springblade.attence.ruleperson.service.IRulePersonService;
import org.springblade.attence.rulespectime.entity.RuleSpecTime;
import org.springblade.attence.rulespectime.service.IRuleSpecTimeService;
import org.springblade.attence.ruletime.entity.RuleTime;
import org.springblade.attence.ruletime.service.IRuleTimeService;
import org.springblade.community.accessrecord.entity.AccessRecord;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestParam;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.attence.record.entity.Record;
import org.springblade.attence.record.vo.RecordVO;
import org.springblade.attence.record.wrapper.RecordWrapper;
import org.springblade.attence.record.service.IRecordService;
import org.springblade.core.boot.ctrl.BladeController;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 控制器
 *
 * @author BladeX
 * @since 2021-04-12
 */
@RestController
@AllArgsConstructor
@RequestMapping("attence/record")
@Api(value = "打卡记录接口", tags = "打卡记录接口")
public class RecordController extends BladeController {

	private IRecordService recordService;
	private IRuleService ruleService;
	private IRulePersonService rulePersonService;
	private IRuleTimeService ruleTimeService;
	private IRuleSpecTimeService ruleSpecTimeService;


	/**
	 * 详情
	 */
	@GetMapping("/detail")
	@ApiOperationSupport(order = 1)
	@ApiOperation(value = "详情", notes = "传入record")
	public R<RecordVO> detail(Record record) {
		Record detail = recordService.getOne(Condition.getQueryWrapper(record));
		return R.data(RecordWrapper.build().entityVO(detail));
	}

	/**
	 * 分页
	 */
	@GetMapping("/list")
	@ApiOperationSupport(order = 2)
	@ApiOperation(value = "分页", notes = "传入record")
	public R<IPage<RecordVO>> list(Record record, Query query) {
		IPage<Record> pages = recordService.page(Condition.getPage(query), Condition.getQueryWrapper(record));
		return R.data(RecordWrapper.build().pageVO(pages));
	}


	/**
	 * 自定义分页
	 */
	@GetMapping("/page")
	@ApiOperationSupport(order = 3)
	@ApiOperation(value = "分页", notes = "传入record")
	public R<IPage<RecordVO>> page(RecordVO record, Query query) {
		IPage<RecordVO> pages = recordService.selectRecordPage(Condition.getPage(query), record);
		return R.data(pages);
	}

	/**
	 * 新增
	 */
	@PostMapping("/save")
	@ApiOperationSupport(order = 4)
	@ApiOperation(value = "新增", notes = "传入record")
	public R save(@Valid @RequestBody Record record) {
		record.setCheckinDate(record.getCheckinTime().toLocalDate());
		return R.status(recordService.save(record));
	}

	/**
	 * 修改
	 */
	@PostMapping("/update")
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "修改", notes = "传入record")
	public R update(@Valid @RequestBody Record record) {
		return R.status(recordService.updateById(record));
	}

	/**
	 * 新增或修改
	 */
	@PostMapping("/submit")
	@ApiOperationSupport(order = 6)
	@ApiOperation(value = "新增或修改", notes = "传入record")
	public R submit(@Valid @RequestBody Record record) {
		record.setCheckinDate(record.getCheckinTime().toLocalDate());
		return R.status(recordService.saveOrUpdate(record));
	}


	/**
	 * 删除
	 */
	@PostMapping("/remove")
	@ApiOperationSupport(order = 7)
	@ApiOperation(value = "逻辑删除", notes = "传入ids")
	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
		return R.status(recordService.deleteLogic(Func.toLongList(ids)));
	}
	/**
	 * 根据日期获取某人的打卡记录
	 */
	@GetMapping("/getRecordsByDate")
	@ApiOperationSupport(order = 10)
	@ApiOperation(value = "根据日期获取某人的打卡记录", notes = "传入record")
	public R getRecordsByDate(Record record) {
		Assert.notNull(record.getPersonId(), "personId不能为空");
		Assert.notNull(record.getPersonType(), "打卡人员类型不能为空");

		Date date = new Date();
		if (record.getCheckinTime()!=null) {
			date = Date.from(record.getCheckinTime().atZone(ZoneId.systemDefault()).toInstant());
		}
		List<Record> recordList = recordService.list(Condition.getQueryWrapper(new Record()).lambda()
			.eq(Record::getPersonId,record.getPersonId())
			.eq(Record::getPersonType,record.getPersonType())
			.ge(Record::getCheckinTime, DateUtil.beginOfDay(date))
			.le(Record::getCheckinTime,DateUtil.endOfDay(date))
		);
		return R.data(recordList);
	}



	/**
	 * 获取某月的打卡标记
	 */
	@GetMapping("/getMarkByDate")
	@ApiOperationSupport(order = 8)
	@ApiOperation(value = "获取某月的打卡标记", notes = "获取某月的打卡标记")
	public R getMarkByDate(RulePerson rulePerson, LocalDate date) {
		List<MarkDayVo> markDayVoList = recordService.getMarkByDate(rulePerson, date);
		return R.data(markDayVoList);
	}

	/**
	 * 获取员工的打卡统计
	 * 出勤天数
	 * 正常记录
	 * 迟到记录
	 * 早退记录
	 * 范围外打卡异常
	 * 旷工异常记录
	 * 缺卡记录
	 */
	@GetMapping("/getPersonStatistical")
	@ApiOperationSupport(order = 9)
	@ApiOperation(value = "员工打卡统计", notes = "员工打卡统计")
	public  R getPersonStatistical(RulePerson rulePerson,LocalDate date){
		Assert.notNull(rulePerson.getPersonId(), "personId不能为空");
		Assert.notNull(rulePerson.getPersonType(), "personType不能为空");
		Assert.notNull(date, "查询的日期不能为空");

		MyStatisticalVo personStatistical = recordService.getPersonStatistical(rulePerson, date);
		return R.data(personStatistical);
	}



	/**
	 * 根据月获取打卡记录
	 */
	@GetMapping("/getRecordByMonth")
	@ApiOperationSupport(order = 11)
	@ApiOperation(value = "根据月获取打卡记录", notes = "传入recordVO")
	public R<List<RecordAppVO>> getRecordByMonth(RecordVO recordVO) throws ParseException {
		Assert.notNull(recordVO.getPersonId(), "personId为空");
		Assert.notNull(recordVO.getSearchDate(),"searchDate为空");
		//时间转化
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM");
		Date month = dateFormat.parse(recordVO.getSearchDate());
		//获取月的天数
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(month);
		//根据人员的id和时间获取月的打卡记录
		List<Record> records = this.recordService.list(Condition.getQueryWrapper(new Record()).lambda()
			.eq(Record::getPersonId,recordVO.getPersonId())
			.ge(Record::getCheckinTime, DateUtil.beginOfMonth(month))
			.le(Record::getCheckinTime, DateUtil.endOfMonth(month))
			.orderByAsc(Record::getCheckinDate));
		//获取和人员绑定的打卡规则
		RulePerson rulePerson = this.rulePersonService.getOne(Condition.getQueryWrapper(new RulePerson()).lambda()
			.eq(RulePerson::getPersonId, recordVO.getPersonId())
			.eq(RulePerson::getType,0));
		//获取打卡规则
		Rule rule = this.ruleService.getById(rulePerson.getRuleId());
		//获取打卡时间
		List<RuleTime> ruleTimes = this.ruleTimeService.list(Condition.getQueryWrapper(new RuleTime()).lambda()
			.eq(RuleTime::getRuleId, rule.getId()));

		String[] workDays = ruleTimes.stream().map(RuleTime::getWorkDays).collect(Collectors.joining(",")).split(",");
		Arrays.sort(workDays);
		//获取特殊打卡时间
		List<RuleSpecTime> specTimes = this.ruleSpecTimeService.list(Condition.getQueryWrapper(new RuleSpecTime()).lambda()
			.eq(RuleSpecTime::getRuleId, rule.getId()));
		List<String> mustDates = specTimes.stream().filter(item-> item.getIsMust() == 1).map(item-> item.getDate().toString()).collect(Collectors.toList());
		List<String> notMustDates = specTimes.stream().filter(item-> item.getIsMust() == 0).map(item-> item.getDate().toString()).collect(Collectors.toList());
		//获取月的天数
		Calendar instance = Calendar.getInstance();
		int days = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
		List<RecordAppVO> recordAppVOS = new ArrayList<>();
		for (int i= 1; i<= days; i++){
			RecordAppVO recordAppVO = new RecordAppVO();
			String date = null;
			if (i < 10){
				date = recordVO.getSearchDate() + '-' + '0' + i;
			}else {
				date = recordVO.getSearchDate() + '-' + i;
			}
			recordAppVO.setDate(date);
			instance.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(date));
			int dayOfWeek = instance.get(Calendar.DAY_OF_WEEK)-1;
			if (!notMustDates.contains(date) && Arrays.binarySearch(workDays,String.valueOf(dayOfWeek)) >= 0){
				this.setRecordAppVo(records, recordAppVO, date);
			}else if (mustDates.contains(date)){
				this.setRecordAppVo(records, recordAppVO, date);
			}else {
				recordAppVO.setNormal(true);
			}
			recordAppVOS.add(recordAppVO);
		}

		return R.data(recordAppVOS);
	}

	public void setRecordAppVo(List<Record> records, RecordAppVO recordAppVO, String date) {
		for (Record record : records){
			if (recordAppVO.getWorkRecord() != null && recordAppVO.getOffWorkRecord() != null){
				recordAppVO.setNormal(true);
				LocalDateTime work = recordAppVO.getWorkRecord().getCheckinTime();
				LocalDateTime offWork = recordAppVO.getOffWorkRecord().getCheckinTime();
				long hours = ChronoUnit.HOURS.between(work,offWork);
				long minutes = ChronoUnit.MINUTES.between(work,offWork);
				recordAppVO.setWorkTime(hours + "时" + minutes + "分");
				break;
			}else {
				recordAppVO.setNormal(false);
			}
			String checkinDate = record.getCheckinDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
			if (checkinDate.equals(date)){
				if (record.getCheckinType().equals(0)){
					recordAppVO.setWorkRecord(record);
					records.remove(record);
				}else if (record.getCheckinType().equals(1)){
					recordAppVO.setOffWorkRecord(record);
					records.remove(record);
				}
			}
		}
	}
}
