package org.sky.scientific.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.sky.core.log.exception.ServiceException;
import org.sky.core.secure.utils.SecureUtil;
import org.sky.core.tool.api.R;
import org.sky.core.tool.utils.DateUtil;
import org.sky.core.tool.utils.Func;
import org.sky.scientific.mapper.AttendanceMapper;
import org.sky.scientific.mapper.TechnicianMapper;
import org.sky.scientific.mapper.XmMapper;
import org.sky.scientific.mapper.XmTechnicianHoursMapper;
import org.sky.scientific.pojo.dto.HoursSyncDto;
import org.sky.scientific.pojo.entity.AttendanceEntity;
import org.sky.scientific.pojo.entity.TechnicianEntity;
import org.sky.scientific.pojo.entity.XmTechnicianHours;
import org.sky.scientific.pojo.vo.XmVO;
import org.sky.scientific.service.IHoursSyncService;
import org.sky.scientific.service.IXmService;
import org.sky.system.feign.ISysClient;
import org.sky.system.pojo.entity.Tenant;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.*;
import java.util.stream.Collectors;

@Data
@Slf4j
@Service
@AllArgsConstructor
public class HoursSyncServiceImpl implements IHoursSyncService {

	private final XmTechnicianHoursMapper hoursMapper;
	private final RestTemplate restTemplate;
	private final ISysClient sysClient;
	private final XmMapper xmMapper;
	private final IXmService xmService;
	private final TechnicianMapper technicianMapper;
	private final AttendanceMapper attendanceMapper;
	private static final String BASE_URL = "https://yanfa.jishutao.com/prod-api";
	private static final String URL_LOGIN = BASE_URL + "/login";
	private static final String URL_SYNC_HOURS = BASE_URL + "/api/project/pushCompanyData";
	private static final String URL_ACCEPT_HOURS = BASE_URL + "/api/project/getCompanyClock";

	private static final String HEAD_AUTHORIZATION = "Authorization";
	private static final String USERNAME = "yandongli";
	private static final String PASSWORD = "123456";
	private static final Integer BATCH_SIZE = 100;

	@Override
	public boolean syncHoursData(HoursSyncDto dto) {
		//先登录
		String token = this.login();
		if (token != null) {
			JSONObject reqBody = getBasicInfo(dto.getYearAndMonth());

			List<JSONObject> hoursList = hoursMapper.selectListForSync(dto.getYearAndMonth());
			List<JSONObject> rdList = new ArrayList<>();
			for (JSONObject hours : hoursList) {
				JSONObject data = new JSONObject();
				data.put("projectNumber", hours.getString("xmbh"));
				data.put("adminKyId", SecureUtil.getTenantId() + "_" + hours.getString("unicode"));
				data.put("recordTime", DateUtil.format(DateUtil.parse(hours.getString("workDate"), DateUtil.PATTERN_YYYYMMDD), DateUtil.PATTERN_DATE));
				data.put("duration", hours.getString("workHours"));
				rdList.add(data);
			}
			reqBody.put("usci", SecureUtil.getTenantId());
			reqBody.put("rdList", rdList);
			log.info("同步至工时系统：{}", JSON.toJSONString(reqBody));
			HttpHeaders headers = new HttpHeaders();
			headers.setContentType(MediaType.APPLICATION_JSON);
			headers.add(HEAD_AUTHORIZATION, token);
			HttpEntity<JSONObject> request = new HttpEntity<>(reqBody, headers);
			ResponseEntity<String> response = restTemplate.exchange(URL_SYNC_HOURS, HttpMethod.POST, request, String.class);
			if (response.getStatusCode().is2xxSuccessful()) {
				JSONObject resp = JSONObject.parseObject(response.getBody());
				assert resp != null;
				Integer code = resp.getInteger("code");
				if (code == 200) {
					log.info("成功同步至工时系统");
				} else {
					log.error("同步至工时系统失败：{}", resp.getString("msg"));
					throw new ServiceException("同步至工时系统失败：" + resp.getString("msg"));
				}
			} else {
				throw new ServiceException("同步至工时系统失败：" + response.getStatusCode());
			}
		}
		return true;
	}

	private JSONObject getBasicInfo(String yearAndMonth) {
		JSONObject reqBody = new JSONObject();

		List<JSONObject> listDep = new ArrayList<>();
		JSONObject dep = new JSONObject();
		R<Tenant> tenant = sysClient.getTenant(SecureUtil.getTenantId());
		if (tenant.isSuccess()) {
			dep.put("kyId", String.valueOf(tenant.getData().getId()));
			dep.put("deptName", tenant.getData().getTenantName());
			dep.put("kySuperId", null);
		}
		dep.put("usci", SecureUtil.getTenantId());
		listDep.add(dep);

		List<JSONObject> listUser = new ArrayList<>();
		List<JSONObject> hoursList = hoursMapper.selectListForSync(yearAndMonth);
		if (Func.isEmpty(hoursList)) {
			throw new ServiceException("无数据可同步");
		}
		List<String> unicodeList = hoursList.stream().map(rd -> rd.getString("unicode")).distinct().toList();
		for (String unicode : unicodeList) {
			JSONObject user = new JSONObject();
			user.put("kyDeptId", dep.get("kyId"));
			user.put("userName", SecureUtil.getTenantId() + "_" + unicode);
			TechnicianEntity dbTechnician = technicianMapper.selectByUnicodeAndYearAndMonth(unicode, yearAndMonth);
			if (Func.notNull(dbTechnician)) {
				user.put("nickName", dbTechnician.getName());
			}
			user.put("kyId", user.getString("userName"));
			user.put("kySuperId", null);
			listUser.add(user);
		}

		List<Long> xmIdList = hoursList.stream().map(rd -> rd.getLong("xmId")).distinct().toList();
		List<XmVO> xmList = xmMapper.selectByXmIdList(xmIdList);
		Map<Long, List<String>> mapByXmId = hoursList.stream().collect(Collectors.groupingBy(rd -> rd.getLong("xmId"), Collectors.mapping(rd -> rd.getString("unicode"), Collectors.toList())));

		List<JSONObject> listUserProject = new ArrayList<>();
		for (XmVO xmVO : xmList) {
			JSONObject data = new JSONObject();
			data.put("projectName", xmVO.getXmmc());
			TechnicianEntity dbTechnician = technicianMapper.selectById(xmVO.getXmfzr());
			if (Func.notNull(dbTechnician)) {
				String username = SecureUtil.getTenantId() + "_" + dbTechnician.getUnicode();
				if (listUser.stream().noneMatch(user -> user.getString("userName").equals(username))) {
					JSONObject user = new JSONObject();
					user.put("kyDeptId", dep.get("kyId"));
					user.put("userName", username);
					user.put("nickName", dbTechnician.getName());
					user.put("kyId", username);
					user.put("kySuperId", null);
					listUser.add(user);
				}
				data.put("aid", username);
			}

			data.put("projectNumber", xmVO.getXmbh());
			data.put("startTime", DateUtil.format(xmVO.getXmkssj(), DateUtil.PATTERN_DATE));
			data.put("endTime", DateUtil.format(xmVO.getXmjssj(), DateUtil.PATTERN_DATE));
			data.put("projectYear", DateUtil.format(xmVO.getXmkssj(), DateUtil.PATTERN_YYYY));
			data.put("createYear", DateUtil.format(xmVO.getCreateTime(), DateUtil.PATTERN_YYYY));
			List<String> unicodes = mapByXmId.get(xmVO.getId());
			data.put("staffIds", unicodes.stream().distinct().map(unicode -> SecureUtil.getTenantId() + "_" + unicode).collect(Collectors.joining(",")));
			listUserProject.add(data);
		}

		reqBody.put("listDep", listDep);
		reqBody.put("listUser", listUser);
		reqBody.put("listUserProject", listUserProject);

		return reqBody;
	}

	@Override
	@Transactional
	public boolean acceptRdData(HoursSyncDto dto) {
		//先登录
		String token = this.login();
		if (token != null) {
			String url = UriComponentsBuilder.fromHttpUrl(URL_ACCEPT_HOURS)
				.queryParam("usci", SecureUtil.getTenantId())
				.queryParam("year", Integer.valueOf(dto.getYearAndMonth().substring(0, 4)))
				.queryParam("month", Integer.valueOf(dto.getYearAndMonth().substring(4, 6))).toUriString();
			HttpHeaders headers = new HttpHeaders();
			headers.setContentType(MediaType.APPLICATION_JSON);
			headers.add(HEAD_AUTHORIZATION, token);
			HttpEntity<JSONObject> request = new HttpEntity<>(headers);
			ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, request, String.class);
			if (response.getStatusCode().is2xxSuccessful()) {
				JSONObject resp = JSONObject.parseObject(response.getBody());
				assert resp != null;
				Integer code = resp.getInteger("code");
				if (code == 200) {
					JSONObject data = resp.getJSONObject("data");
					JSONArray rdList = data.getJSONArray("rdList");
					if(rdList.isEmpty()){
						throw new ServiceException("无数据可同步");
					}
					Map<String, Long> xmMap = new HashMap<>();

					List<JSONObject> dbHoursList = hoursMapper.selectListForSync(dto.getYearAndMonth());
					List<String> identifierList = dbHoursList.stream().map(t ->
						t.getString("tenantId") + ":" + t.getString("xmId") + ":" + t.getString("unicode") + ":" + t.getString("workDate")).toList();

					Map<String, List<JSONObject>> mapByUnicode = dbHoursList.stream().collect(Collectors.groupingBy(t -> t.getString("unicode"), Collectors.toList()));

					List<XmTechnicianHours> insertList = new ArrayList<>();
					List<XmTechnicianHours> updateList = new ArrayList<>();

					Map<String, String> userNameMap = new HashMap<>();
					for (Object o : rdList) {
						JSONObject rd = (JSONObject) o;
						Double duration = rd.getDouble("duration");

						String adminKyId = rd.getString("adminKyId");
						List<String> tenantAndUnicode = Arrays.asList(adminKyId.split("_"));

						String unicode = tenantAndUnicode.get(1);

						String recordTime = rd.getString("recordTime");
						recordTime = DateUtil.format(DateUtil.parse(recordTime, DateUtil.PATTERN_YYYY_MM_DD), DateUtil.PATTERN_YYYYMMDD);

						String projectNumber = rd.getString("projectNumber");
						if (!xmMap.containsKey(projectNumber)) {
							Long xmId = xmMapper.getXmIdByXmbh(projectNumber);
							if (xmId == null) {
								xmId = 0L;
							}
							xmMap.put(projectNumber, xmId);
						}
						Long xmId = xmMap.get(projectNumber);
						if (xmId <= 0L) {
							continue;
						}
//						if (xmService.judgeXmIsOver(xmId, false)) {
//							continue;
//						}

						String identifier = SecureUtil.getTenantId() + ":" + xmId + ":" + unicode + ":" + recordTime;

						List<JSONObject> hourListByUnicode = mapByUnicode.get(unicode);
						if (hourListByUnicode == null) {
							hourListByUnicode = new ArrayList<>();
						}
						mapByUnicode.put(unicode, hourListByUnicode);

						for (JSONObject t : hourListByUnicode) {
							if (Func.equals(t.getLong("xmId"), xmId) && Func.equals(t.getString("unicode"), unicode) && Func.equals(t.getString("workDate"), recordTime)) {
								t.put("workHours", duration);
								break;
							}
						}

						String userName = userNameMap.get(unicode);
						if (userName == null) {
							userName = technicianMapper.getNameByUnicodeAndMonth(unicode, recordTime.substring(0, 6));
							userNameMap.put(unicode, userName);
						}

						//判断用户当天是否有考勤数据，且用户的总工时不能超过出勤时长
						AttendanceEntity entity = new AttendanceEntity();
						entity.setUnicode(unicode);
						entity.setWorkDate(recordTime);
						AttendanceEntity one = attendanceMapper.selectInfoByUnicodeAndDate(entity);
						if (Func.isNull(one)) {
							throw new ServiceException(userName + "在" + recordTime + "这天没有考勤数据");
						}

						List<JSONObject> list = mapByUnicode.get(unicode);
						String finalRecordTime = recordTime;
						double sum = list.stream().filter(t -> Func.equals(finalRecordTime, t.getString("workDate"))).mapToDouble(t -> t.getDouble("workHours")).sum();
						if (sum > one.getWorkHours()) {
							throw new ServiceException(one.getUserName() + "在" + recordTime + "这天的考勤时长为" + one.getWorkHours() + "小时,填报的工时为" + sum + "小时，同步失败");
						}
						if (identifierList.contains(identifier)) {
							//现有的，做修改
							XmTechnicianHours updateEntity = new XmTechnicianHours();
							updateEntity.setTenantId(SecureUtil.getTenantId());
							updateEntity.setXmId(xmId);
							updateEntity.setUnicode(unicode);
							updateEntity.setWorkDate(recordTime);
							updateEntity.setWorkHours(duration);
							updateList.add(updateEntity);

						} else {
							//新的，做插入
							XmTechnicianHours insertEntity = new XmTechnicianHours();
							insertEntity.setTenantId(SecureUtil.getTenantId());
							insertEntity.setXmId(xmId);
							insertEntity.setUnicode(unicode);
							insertEntity.setWorkDate(recordTime);
							insertEntity.setWorkHours(duration);
							insertList.add(insertEntity);
						}
					}

					for (int i = 0; i < insertList.size(); i +=BATCH_SIZE) {
						int end = Math.min(insertList.size(), i + BATCH_SIZE);
  						hoursMapper.insert(insertList.subList(i, end));
					}

					for (int i = 0; i < updateList.size(); i += BATCH_SIZE) {
						int end = Math.min(updateList.size(), i + BATCH_SIZE);
						hoursMapper.batchUpdateHours(updateList.subList(i, end));
					}
					log.info("同步至本系统，响应数据：{}", data);
				} else {
					log.error("同步至本系统失败：{}", resp.getString("msg"));
					throw new ServiceException("同步至本系统失败，" + resp.getString("msg"));
				}
			} else {
 				throw new ServiceException("同步至本系统失败，请联系管理员");
			}
		}
		return true;
	}

	private String login() {
		try {
			HttpHeaders headers = new HttpHeaders();
			headers.setContentType(MediaType.APPLICATION_JSON);
			JSONObject loginBody = new JSONObject();
			loginBody.put("username", USERNAME);
			loginBody.put("password", PASSWORD);
			HttpEntity<JSONObject> request = new HttpEntity<>(loginBody, headers);
			ResponseEntity<String> response = restTemplate.exchange(URL_LOGIN, HttpMethod.POST, request, String.class);
			if (response.getStatusCode().is2xxSuccessful()) {
				JSONObject resp = JSONObject.parseObject(response.getBody());
				assert resp != null;
				Integer code = resp.getInteger("code");
				if (code == 200) {
					String token = resp.getString("token");
					log.info("成功登录工时系统：{}", token);
					return token;
				} else {
					log.error("登录工时系统失败：{}", resp.getString("msg"));
					throw new ServiceException("登录工时系统失败：" + resp.getString("msg"));
				}
			} else {
				throw new ServiceException("登录工时系统失败：" + response.getStatusCode());
			}
		} catch (RestClientException e) {
			log.error("请求失败: {}", e.getMessage());
			throw new ServiceException("请求失败：" + e.getMessage());
		}
	}

}
