/*
 *      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.rule.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springblade.attence.location.entity.Location;
import org.springblade.attence.location.service.ILocationService;
import org.springblade.attence.record.entity.Record;
import org.springblade.attence.record.service.IRecordService;
import org.springblade.attence.rule.dto.QueryDataDTO;
import org.springblade.attence.rule.entity.Rule;
import org.springblade.attence.rule.vo.AttenceRuleVo;
import org.springblade.attence.rule.vo.RuleVO;
import org.springblade.attence.rule.mapper.RuleMapper;
import org.springblade.attence.rule.service.IRuleService;
import org.springblade.attence.rule.vo.WxAppDataVo;
import org.springblade.attence.ruleout.entity.RuleOut;
import org.springblade.attence.ruleout.service.IRuleOutService;
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.attence.wifi.entity.Wifi;
import org.springblade.attence.wifi.service.IWifiService;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.utils.DistanceUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.util.Assert;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 *  服务实现类
 *
 * @author BladeX
 * @since 2021-04-12
 */
@Service
public class RuleServiceImpl extends BaseServiceImpl<RuleMapper, Rule> implements IRuleService {

	private final ILocationService locationService;
	private final IWifiService wifiService;
	private final IRuleTimeService ruleTimeService;
	private final IRuleSpecTimeService ruleSpecTimeService;
	private final IRulePersonService rulePersonService;
	private final IRecordService recordService;
	private final IRuleOutService ruleOutService;



	@Autowired
	public  RuleServiceImpl(ILocationService locationService, IWifiService wifiService, IRuleTimeService ruleTimeService,
							IRuleSpecTimeService ruleSpecTimeService, IRulePersonService rulePersonService, IRecordService recordService, IRuleOutService ruleOutService){
		this.locationService = locationService;
		this.wifiService = wifiService;
		this.ruleTimeService = ruleTimeService;
		this.ruleSpecTimeService = ruleSpecTimeService;
		this.rulePersonService = rulePersonService;
		this.recordService = recordService;
		this.ruleOutService = ruleOutService;
	}

	@Override
	public IPage<RuleVO> selectRulePage(IPage<RuleVO> page, RuleVO rule) {
		return page.setRecords(baseMapper.selectRulePage(page, rule));
	}

	@Override
	public AttenceRuleVo getAttenceRuleVo(Rule rule) {
		AttenceRuleVo attenceRuleVo = new AttenceRuleVo();
		Rule baseRule = this.baseMapper.selectById(rule.getId());

		BeanUtil.copyProperties(baseRule,attenceRuleVo);
		List<Location> locations = this.locationService.list(Condition.getQueryWrapper(new Location()).lambda().eq(Location::getRuleId, rule.getId()));
		attenceRuleVo.setLocations(locations);
		List<Wifi> wifiList = this.wifiService.list(Condition.getQueryWrapper(new Wifi()).lambda().eq(Wifi::getRuleId, rule.getId()));
		attenceRuleVo.setWifiList(wifiList);
		List<RuleTime> ruleTimes = this.ruleTimeService.list(Condition.getQueryWrapper(new RuleTime()).lambda().eq(RuleTime::getRuleId, rule.getId()));
		attenceRuleVo.setRuleTimes(ruleTimes);
		List<RuleSpecTime> ruleSpecTimes = this.ruleSpecTimeService.list(Condition.getQueryWrapper(new RuleSpecTime()).lambda().eq(RuleSpecTime::getRuleId, rule.getId()));
		attenceRuleVo.setRuleSpecTimes(ruleSpecTimes);
		//外出打卡规则
		RuleOut ruleOut = ruleOutService.getOne(Condition.getQueryWrapper(new RuleOut()).lambda()
			.eq(RuleOut::getOwnerId, baseRule.getOwnerId())
			.eq(RuleOut::getOwnerType, baseRule.getOwnerType())
		);
		attenceRuleVo.setRuleOut(ruleOut);
		//当天是否是工作日
		WxAppDataVo necessaryDateData = this.getNecessaryDateData(rule.getId());
		attenceRuleVo.setIsWork(necessaryDateData.getIsWork());

		return attenceRuleVo;
	}

	@Override
	public WxAppDataVo getNecessaryData(QueryDataDTO queryDataDTO) {
		//根据person_id和person_type获取到rule_id
		QueryWrapper<RulePerson> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("person_id", queryDataDTO.getPersonId());
		queryWrapper.eq("person_type", queryDataDTO.getPersonType());
		queryWrapper.eq("type", queryDataDTO.getPersonType());

		RulePerson rulePerson = rulePersonService.getOne(queryWrapper);
		Assert.notNull(rulePerson, "该人员未绑定打卡规则");

		long ruleId = rulePerson.getRuleId();

		//判断是否是工作日
		WxAppDataVo necessaryDateData = this.getNecessaryDateData(ruleId);
		if (!necessaryDateData.getIsWork()) {
			//不是工作日直接返回
			return necessaryDateData;
		}
		WxAppDataVo wxAppDataVo = new WxAppDataVo();
		BeanUtil.copyProperties(necessaryDateData,wxAppDataVo);

		//判断是否在指定区域内
		WxAppDataVo necessaryLocationData = this.getNecessaryLocationData(queryDataDTO, ruleId);
		wxAppDataVo.setNeedLocation(necessaryLocationData.getNeedLocation());
		wxAppDataVo.setIsInPunchLocation(necessaryLocationData.getIsInPunchLocation());

		//判断是否在指定wifi区域内
		WxAppDataVo necessaryWifiData = this.getNecessaryWifiData(queryDataDTO, ruleId);
		wxAppDataVo.setNeedWifi(necessaryWifiData.getNeedWifi());
		wxAppDataVo.setIsInPunchWifi(necessaryWifiData.getIsInPunchWifi());

		//获取当天打卡记录
		WxAppDataVo necessaryRecordsData =this.getNecessaryRecordsData(queryDataDTO,wxAppDataVo.getOffWorkTime());
		wxAppDataVo.setWorkPunchRecords(necessaryRecordsData.getWorkPunchRecords());
		wxAppDataVo.setOffWorkPunchRecords(necessaryRecordsData.getOffWorkPunchRecords());
		wxAppDataVo.setIsDone(necessaryRecordsData.getIsDone());
		wxAppDataVo.setWorkStatus(necessaryRecordsData.getWorkStatus());

		return wxAppDataVo;
	}
	/**
	 * 获取当天打卡记录
	 * @param queryDataDTO
	 * @return
	 */
	private WxAppDataVo getNecessaryRecordsData(QueryDataDTO queryDataDTO, LocalTime offWorkTime) {
		WxAppDataVo wxAppDataVo = new WxAppDataVo();
		List<Record> recordList = recordService.list(Condition.getQueryWrapper(new Record()).lambda()
			.eq(Record::getPersonId, queryDataDTO.getPersonId())
			.eq(Record::getPersonType, queryDataDTO.getPersonType())
			.ge(Record::getCheckinTime, DateUtil.beginOfDay(new Date()))
			.le(Record::getCheckinTime, DateUtil.endOfDay(new Date()))
		);
		//筛选上班打卡记录
		List<Record> workPunchRecords = recordList.stream().filter(record -> record.getCheckinType() == 0).collect(Collectors.toList());
		//筛选下班打卡记录
		List<Record> offWorkPunchRecords = recordList.stream().filter(record -> record.getCheckinType() == 1).collect(Collectors.toList());

		if (!CollUtil.isEmpty(offWorkPunchRecords)) {
			//下班打卡不为空
			wxAppDataVo.setIsDone(true);
		}

		wxAppDataVo.setWorkPunchRecords(workPunchRecords);
		wxAppDataVo.setOffWorkPunchRecords(offWorkPunchRecords);

		//当前时间戳
		long nowTimeStamp= new Date().getTime();
		//下班时间戳
		String s = LocalDate.now().toString()+" "+offWorkTime.toString();
		long offWorkTimeStamp = DateTime.of(s, DatePattern.NORM_DATETIME_MINUTE_PATTERN).getTime();
		//如果上班打卡记录为空，且再下班时间之前，都视为上班打卡
		if (CollUtil.isEmpty(workPunchRecords) && nowTimeStamp<offWorkTimeStamp) {
			wxAppDataVo.setWorkStatus(0);
		}else{
			wxAppDataVo.setWorkStatus(1);
		}
		return wxAppDataVo;
	}
	/**
	 * 判断是否在打卡日期内
	 * @return
	 */
	private WxAppDataVo getNecessaryDateData(long ruleId ) {
		WxAppDataVo wxAppDataVo = new WxAppDataVo();
		//特殊日期
		RuleSpecTime  ruleSpecTime= ruleSpecTimeService.getOne(Condition.getQueryWrapper(new RuleSpecTime()).lambda()
			.eq(RuleSpecTime::getRuleId, ruleId)
			.eq(RuleSpecTime::getDate, LocalDate.now())
		);
		//正常日期
		List<RuleTime> ruleTimeList = ruleTimeService.list(Condition.getQueryWrapper(new RuleTime()).lambda().eq(RuleTime::getRuleId, ruleId));

		if (ruleSpecTime!=null) {
			if (ruleSpecTime.getIsMust() == 1) {
				//当天必须打卡
				wxAppDataVo.setIsWork(true);
				wxAppDataVo.setRuleTimeId(ruleSpecTime.getId());
				wxAppDataVo.setRuleTimeType(0);
				wxAppDataVo.setWorkTime(ruleSpecTime.getBeginTime());
				wxAppDataVo.setOffWorkTime(ruleSpecTime.getEndTime());
			}else{
				//当天不需要打卡
				wxAppDataVo.setIsWork(false);
			}
			return wxAppDataVo;
		}


		for (RuleTime ruleTime :ruleTimeList) {
			List<String> list = Arrays.asList(ruleTime.getWorkDays().split(","));
			//获取今天是星期几
			//这里的周日是1，但数据库存储的周日是0，所以要减去1
			DateTime date = DateUtil.date();
			String i = Integer.toString(DateUtil.dayOfWeek(date)-1);
			if (list.contains(i)) {
				wxAppDataVo.setIsWork(true);
				wxAppDataVo.setRuleTimeId(ruleTime.getId());
				wxAppDataVo.setRuleTimeType(1);
				wxAppDataVo.setWorkTime(ruleTime.getWorkTime());
				wxAppDataVo.setOffWorkTime(ruleTime.getOffWorkTime());
				wxAppDataVo.setRuleTime(ruleTime);
				break;
			} else {
				wxAppDataVo.setIsWork(false);
			}
		}
		return wxAppDataVo;
	}
	/**
	 * 判断是否在指定区域内
	 * @param queryDataDTO
	 * @param ruleId
	 * @return
	 */
	private WxAppDataVo getNecessaryLocationData(QueryDataDTO queryDataDTO,long ruleId) {
		WxAppDataVo wxAppDataVo = new WxAppDataVo();
		List<Location> locationList = locationService.list(Condition.getQueryWrapper(new Location()).lambda().eq(Location::getRuleId, ruleId));
		//如果数组为空，表示该规则不需要位置打卡
		if (CollUtil.isEmpty(locationList)) {
			wxAppDataVo.setNeedLocation(false);
			wxAppDataVo.setIsInPunchLocation(false);
			return wxAppDataVo;
		}
		wxAppDataVo.setNeedLocation(true);
		for (Location location:locationList) {
			//计算两点之间的距离
			double distance = DistanceUtils.getDistance(queryDataDTO.getLongitude(), queryDataDTO.getLatitude(), location.getLongitude(), location.getLatitude());
			double area = location.getArea();
			if (area >= distance) {
				wxAppDataVo.setIsInPunchLocation(true);
				break;
			} else {
				wxAppDataVo.setIsInPunchLocation(false);
			}
		}
		return wxAppDataVo;
	}

	/**
	 * 判断是否在指定wifi区域内
	 * @param queryDataDTO
	 * @param ruleId
	 * @return
	 */
	private WxAppDataVo getNecessaryWifiData(QueryDataDTO queryDataDTO, long ruleId) {
		WxAppDataVo wxAppDataVo = new WxAppDataVo();
		//获取wifi列表
		List<Wifi> wifiList = wifiService.list(Condition.getQueryWrapper(new Wifi()).lambda().eq(Wifi::getRuleId, ruleId));
		if (CollUtil.isEmpty(wifiList)) {
			wxAppDataVo.setNeedWifi(false);
			wxAppDataVo.setIsInPunchWifi(false);
			return wxAppDataVo;
		}
		wxAppDataVo.setNeedWifi(true);
		for (Wifi wifi:wifiList) {
			if (queryDataDTO.getWifiMac().equals(wifi.getMac())) {
				wxAppDataVo.setIsInPunchWifi(true);
				break;
			} else {
				wxAppDataVo.setIsInPunchWifi(false);
			}
		}
		return wxAppDataVo;
	}
}
