package com.goafanti.report.service;

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

import com.goafanti.admin.service.DepartmentService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.ModelMap;
import com.goafanti.common.constant.AFTConstants;
import com.goafanti.common.dao.DailySalesReportMapper;
import com.goafanti.common.dao.UserMapper;
import com.goafanti.common.error.BusinessException;
import com.goafanti.common.model.DailySalesReport;
import com.goafanti.core.mybatis.BaseMybatisDao;
import com.goafanti.report.bo.CountMarketingStatisticsBo;
import com.goafanti.report.bo.DepartmentSalesReportBO;
import com.goafanti.report.bo.PersonalSalesReportBO;
import com.goafanti.report.bo.ReportDate;
import com.goafanti.report.bo.SalesValues;
import com.goafanti.report.bo.SomeTimeMarketingBo;
import com.goafanti.report.bo.marketingESBo;
import com.goafanti.report.bo.technicalDepStatisticsListBo;
import com.goafanti.report.bo.userDataListBo;
import com.goafanti.report.enums.OrderBy;
import com.goafanti.report.enums.SalesReportOrderField;
import com.goafanti.report.enums.TimeType;

@Service
public class SalesReportServiceImpl extends BaseMybatisDao<DailySalesReportMapper> {

	@Autowired
	DailySalesReportMapper dailySalesReportMapper;
	@Autowired
	UserMapper userMapper;
	@Autowired
	DepartmentService	departmentService;

	public int insertDailyReports(Date startTime, Date endTime) {
		List<DailySalesReport> list = dailySalesReportMapper.selectAdmins();
		Map<String, DailySalesReport> map = new HashMap<>();
		for (DailySalesReport dsr : list) {
			dsr.setCreateTime(endTime);
			map.put(dsr.getAdminId(), dsr);
		}
		List<DailySalesReport> reports = dailySalesReportMapper.selectDailySalesReports(startTime, endTime);
		for (DailySalesReport dsr : reports) {
			DailySalesReport item = map.get(dsr.getAdminId());
			if (item != null) {
				item.setOrderAmount(item.getOrderAmount().add(dsr.getOrderAmount()));
				item.setOrderCount(item.getOrderCount() + dsr.getOrderCount());
			}
		}
		dailySalesReportMapper.deleteByDate(endTime);
		return dailySalesReportMapper.insertBatch(list);
	}

	public ModelMap getReportTotal(Date startTime, String depName, String name, String position) {
		ReportDate rd = new ReportDate(startTime);
		ModelMap mm = new ModelMap();
		putField(mm, "dailyTotalCount", "dailyTotalAmount", rd.getDailyStart(), rd.getDailyEnd(), depName, name,
				position);
		putField(mm, "weeklyTotalCount", "weeklyTotalAmount", rd.getWeeklyStart(), rd.getWeeklyEnd(), depName, name,
				position);
		putField(mm, "monthlyTotalCount", "monthlyTotalAmount", rd.getMonthStart(), rd.getMonthEnd(), depName, name,
				position);
		putField(mm, "quarterlyTotalCount", "quarterlyTotalAmount", rd.getQuarterStart(), rd.getQuarterEnd(), depName,
				name, position);
		putField(mm, "annuallyTotalCount", "annuallyTotalAmount", rd.getYearStart(), rd.getYearEnd(), depName, name,
				position);
		long totalCount = dailySalesReportMapper.selectSalesPersonalReportsCount(rd.getDailyStart(), rd.getDailyEnd(),
				depName, name, position);
		if (totalCount == 0) {
			totalCount = dailySalesReportMapper.selectSalesPersonalReportsCount(null, null, depName, name, position);
		}
		mm.put("totalRecordCount", totalCount);
		return mm;
	}

	private void putField(ModelMap mm, String count, String amount, Date start, Date end, String depName, String name,
			String position) {
		ModelMap res = dailySalesReportMapper.selectTotalReports(start, end, depName, name, position);
		mm.put(count, res == null ? 0 : res.get("totalCount"));
		mm.put(amount, res == null ? 0 : res.get("totalAmount"));
	}

	public List<PersonalSalesReportBO> getPersonSalesReports(SalesReportOrderField orderField, OrderBy order,
			Integer pageSize, Integer pageNo, Date startTime, String depName, String name, String position) {
		ReportDate rd = new ReportDate(startTime);
		List<PersonalSalesReportBO> list = null;
		switch (orderField) {
		case DailyOrderAmount:
		case DailyOrderCount:
			list = dailySalesReportMapper.selectSalesPersonalReports(rd.getDailyStart(), rd.getDailyEnd(), pageNo, pageSize,
					orderField.getCode(), order.getCode(), SalesReportOrderField.DailyOrderCount.getCode(),
					SalesReportOrderField.DailyOrderAmount.getCode(), depName, name, position);
			break;
		case WeeklyOrderCount:
		case WeeklyOrderAmount:
			list = dailySalesReportMapper.selectSalesPersonalReports(rd.getWeeklyStart(), rd.getWeeklyEnd(), pageNo,
					pageSize, orderField.getCode(), order.getCode(), SalesReportOrderField.WeeklyOrderCount.getCode(),
					SalesReportOrderField.WeeklyOrderAmount.getCode(), depName, name, position);
			break;
		case MonthlyOrderCount:
		case MonthlyOrderAmount:
			list = dailySalesReportMapper.selectSalesPersonalReports(rd.getMonthStart(), rd.getMonthEnd(), pageNo, pageSize,
					orderField.getCode(), order.getCode(), SalesReportOrderField.MonthlyOrderCount.getCode(),
					SalesReportOrderField.MonthlyOrderAmount.getCode(), depName, name, position);
			break;
		case QuarterlyOrderCount:
		case QuarterlyOrderAmount:
			list = dailySalesReportMapper.selectSalesPersonalReports(rd.getQuarterStart(), rd.getQuarterEnd(), pageNo,
					pageSize, orderField.getCode(), order.getCode(),
					SalesReportOrderField.QuarterlyOrderCount.getCode(),
					SalesReportOrderField.QuarterlyOrderAmount.getCode(), depName, name, position);
			break;
		case AnnuallyOrderCount:
		case AnnuallyOrderAmount:
			list = dailySalesReportMapper.selectSalesPersonalReports(rd.getYearStart(), rd.getYearEnd(), pageNo, pageSize,
					orderField.getCode(), order.getCode(), SalesReportOrderField.AnnuallyOrderCount.getCode(),
					SalesReportOrderField.AnnuallyOrderAmount.getCode(), depName, name, position);
			break;
		}

		if (list.isEmpty()) {
			list = dailySalesReportMapper.selectPersonalByPage(pageNo, pageSize, depName, name, position);
		}
		if (list.isEmpty()) {
			return list;
		}

		Map<String, PersonalSalesReportBO> map = new HashMap<>();
		List<String> ids = list.stream().map(item -> {
			map.put(item.getId(), item);
			return item.getId();
		}).collect(Collectors.toList());

		if (pageSize == null || pageNo == null) {
			// 如果没有分页，则取其他统计数据时返回所有数据
			ids = null;
		}
		getPersonalExtraInfo(map, ids, AFTConstants.DAILY, rd.getDailyStart(), rd.getDailyEnd());
		getPersonalExtraInfo(map, ids, AFTConstants.WEEKLY, rd.getWeeklyStart(), rd.getWeeklyEnd());
		getPersonalExtraInfo(map, ids, AFTConstants.MONTHLY, rd.getMonthStart(), rd.getMonthEnd());
		getPersonalExtraInfo(map, ids, AFTConstants.QUARTERLY, rd.getQuarterStart(), rd.getQuarterEnd());
		getPersonalExtraInfo(map, ids, AFTConstants.ANNUALLY, rd.getYearStart(), rd.getYearEnd());

		return list;
	}

	private void getPersonalExtraInfo(Map<String, PersonalSalesReportBO> map, List<String> ids, String field,
			Date start, Date end) {
		List<ModelMap> mms = dailySalesReportMapper.selectSalesPersonalExtraReports(start, end, ids);
		for (ModelMap mm : mms) {
			Optional<SalesValues> svo = Optional.ofNullable(map.get(mm.get("id"))).map(it -> it.getValues())
					.map(it -> it.get(field));
			if (svo.isPresent()) {
				svo.get().setOrderAmount((BigDecimal) mm.get("order_amount"));
				svo.get().setOrderCount(((BigDecimal) mm.get("order_count")).intValue());
			}
		}
	}

	public List<DepartmentSalesReportBO> getDepartmentSalesReports(Date startTime) {
		ReportDate rd = new ReportDate(startTime);
		List<DepartmentSalesReportBO> list = dailySalesReportMapper.selectSalesDepartmentReports();
		Map<String, DepartmentSalesReportBO> map = new HashMap<>();

		for (DepartmentSalesReportBO dsr : list) {
			map.put(dsr.getId(), dsr);
		}

		getDepartmentExtraInfo(map, AFTConstants.DAILY, rd.getDailyStart(), rd.getDailyEnd());
		getDepartmentExtraInfo(map, AFTConstants.WEEKLY, rd.getWeeklyStart(), rd.getWeeklyEnd());
		getDepartmentExtraInfo(map, AFTConstants.MONTHLY, rd.getMonthStart(), rd.getMonthEnd());
		getDepartmentExtraInfo(map, AFTConstants.QUARTERLY, rd.getQuarterStart(), rd.getQuarterEnd());
		getDepartmentExtraInfo(map, AFTConstants.ANNUALLY, rd.getYearStart(), rd.getYearEnd());

		for (DepartmentSalesReportBO dsr : list) {
			if (StringUtils.isNotBlank(dsr.getSuperId()) && !StringUtils.equals(dsr.getId(), dsr.getSuperId())) {
				dsr.setLevel(calcLevel(map, dsr.getSuperId(), 0));
			}
		}

		list.sort(new Comparator<DepartmentSalesReportBO>() {
			@Override
			public int compare(DepartmentSalesReportBO o1, DepartmentSalesReportBO o2) {
				// 排序确保父节点在前，不重复计算父级数据
				return o1.getLevel() - o2.getLevel();
			}
		});

		for (DepartmentSalesReportBO dsr : list) {
			if (StringUtils.isNotBlank(dsr.getSuperId()) && !StringUtils.equals(dsr.getId(), dsr.getSuperId())) {
				addToParent(map, dsr.getValues(), dsr.getMemberCount(), dsr.getSuperId());
			}
		}

		return list;
	}

	private int calcLevel(Map<String, DepartmentSalesReportBO> map, String parentId, int level) {
		DepartmentSalesReportBO dsr = map.get(parentId);
		if (dsr != null) {
			level++;
			if (level > 5) {
				// 业务只能有3层，代码定义强行5层跳出，超过5层可能出现上下级循环嵌套的错误数据，导致递归死循环
				throw new BusinessException("部门数据层级错误，请检查数据: " + parentId);
			}
			if (StringUtils.isNotBlank(dsr.getSuperId()) && !StringUtils.equals(dsr.getId(), dsr.getSuperId())) {
				return calcLevel(map, dsr.getSuperId(), level);
			}
		}
		return level;
	}

	/**
	 * 递归给所有上级增加销售统计
	 */
	private void addToParent(Map<String, DepartmentSalesReportBO> map, Map<String, SalesValues> values, int memberCount,
			String parentId) {
		DepartmentSalesReportBO dsr = map.get(parentId);
		if (dsr != null) {
			Map<String, SalesValues> pValues = dsr.getValues();
			for (String period : values.keySet()) {
				SalesValues psv = pValues.get(period);
				SalesValues sv = values.get(period);
				psv.setOrderCount(psv.getOrderCount() + sv.getOrderCount());
				psv.setOrderAmount(psv.getOrderAmount().add(sv.getOrderAmount()));
			}
			dsr.setMemberCount(dsr.getMemberCount() + memberCount);
			if (StringUtils.isNotBlank(dsr.getSuperId()) && !StringUtils.equals(dsr.getId(), dsr.getSuperId())) {
				addToParent(map, values, memberCount, dsr.getSuperId());
			}
		}
	}

	private void getDepartmentExtraInfo(Map<String, DepartmentSalesReportBO> map, String field, Date start, Date end) {
		List<ModelMap> mms = dailySalesReportMapper.selectSalesDepartmentExtraReports(start, end);
		for (ModelMap mm : mms) {
			Optional<SalesValues> svo = Optional.ofNullable(map.get(mm.get("id"))).map(it -> it.getValues())
					.map(it -> it.get(field));
			if (svo.isPresent()) {
				svo.get().setOrderAmount((BigDecimal) mm.get("order_amount"));
				svo.get().setOrderCount(((BigDecimal) mm.get("order_count")).intValue());
			}
		}
	}


	public List<marketingESBo> marketingStatistics(String depId) {
		ReportDate rd = new ReportDate(new Date());
		List<marketingESBo> list=dailySalesReportMapper.findmarketingStatisticsList(depId,rd.getStringDailyStart(),rd.getStringDailyEnd(),rd.getStringWeeklyStart(),rd.getStringWeeklyEnd(),
														rd.getStringMonthStart(),rd.getStringMonthEnd(),rd.getStringYearStart(),rd.getStringYearEnd());

			return list;
	}


	public List<marketingESBo> marketingDepStatistics(String depId) {
		ReportDate rd = new ReportDate(new Date());
		List<marketingESBo> list=dailySalesReportMapper.marketingDepStatisticsList(depId,rd.getStringDailyStart(),rd.getStringDailyEnd(),rd.getStringWeeklyStart(),rd.getStringWeeklyEnd(),
				rd.getStringMonthStart(),rd.getStringMonthEnd(),rd.getStringYearStart(),rd.getStringYearEnd());

		return list;
	}

	public CountMarketingStatisticsBo countMarketingStatistics(String depId) {
		ReportDate rd = new ReportDate(new Date());
		CountMarketingStatisticsBo count=dailySalesReportMapper.privateCustomersCount(depId,rd.getStringDailyStart(),rd.getStringDailyEnd(),rd.getStringWeeklyStart(),rd.getStringWeeklyEnd(),
				rd.getStringMonthStart(),rd.getStringMonthEnd(),rd.getStringYearStart(),rd.getStringYearEnd());
		return count;
	}

	public CountMarketingStatisticsBo countMarketingDepStatistics(String depId) {
		ReportDate rd = new ReportDate(new Date());
		CountMarketingStatisticsBo count=dailySalesReportMapper.privateCustomersDepCount(depId,rd.getStringDailyStart(),rd.getStringDailyEnd(),rd.getStringWeeklyStart(),rd.getStringWeeklyEnd(),
				rd.getStringMonthStart(),rd.getStringMonthEnd(),rd.getStringYearStart(),rd.getStringYearEnd());

		return count;
	}

	public List<SomeTimeMarketingBo> sometimeMarketingStatistics(String depId, Integer timeType, String startTime, String endTime) {
		List<SomeTimeMarketingBo> list = null;
		ReportDate rd = new ReportDate(new Date());
		if (TimeType.WEEKLY.getCode()==timeType) {
			list=dailySalesReportMapper.sometimeMarketingStatistics(depId,rd.getStringWeeklyStart(),rd.getStringWeeklyEnd());
		}
		if (TimeType.MONTH.getCode()==timeType) {
			list=dailySalesReportMapper.sometimeMarketingStatistics(depId,rd.getStringMonthStart(),rd.getStringMonthEnd());
		}
		if (TimeType.QTR.getCode()==timeType) {
			list=dailySalesReportMapper.sometimeMarketingStatistics(depId,rd.getStringQuarterStart(),rd.getStringQuarterEnd());
		}
		if (TimeType.YEAR.getCode()==timeType) {
			list=dailySalesReportMapper.sometimeMarketingStatistics(depId,rd.getStringYearStart(),rd.getStringYearEnd());
		}
		if (TimeType.ZS.getCode()==timeType) {
			list=dailySalesReportMapper.sometimeMarketingStatistics(depId,startTime,endTime);
		}
		return list;
	}

	public List<SomeTimeMarketingBo> sometimeMarketingDepStatistics(String depId, Integer timeType, String startTime, String endTime) {
		List<SomeTimeMarketingBo> list = null;
		ReportDate rd = new ReportDate(new Date());
		if (TimeType.WEEKLY.getCode()==timeType) {
			list=dailySalesReportMapper.sometimeMarketingDepStatistics(depId,rd.getStringWeeklyStart(),rd.getStringWeeklyEnd());
		}
		if (TimeType.MONTH.getCode()==timeType) {
			list=dailySalesReportMapper.sometimeMarketingDepStatistics(depId,rd.getStringMonthStart(),rd.getStringMonthEnd());
		}
		if (TimeType.QTR.getCode()==timeType) {
			list=dailySalesReportMapper.sometimeMarketingDepStatistics(depId,rd.getStringQuarterStart(),rd.getStringQuarterEnd());
		}
		if (TimeType.YEAR.getCode()==timeType) {
			list=dailySalesReportMapper.sometimeMarketingDepStatistics(depId,rd.getStringYearStart(),rd.getStringYearEnd());
		}
		if (TimeType.ZS.getCode()==timeType) {
			list=dailySalesReportMapper.sometimeMarketingDepStatistics(depId,startTime,endTime);
		}
		return list;
	}

	public SomeTimeMarketingBo countSometimeMarketingStatistics(String depId, Integer timeType, String startTime, String endTime) {
		ReportDate rd = new ReportDate(new Date());
		if (TimeType.WEEKLY.getCode()==timeType) {
			return dailySalesReportMapper.countsometimeMarketingStatistics(depId,rd.getStringWeeklyStart(),rd.getStringWeeklyEnd());
		}
		if (TimeType.MONTH.getCode()==timeType) {
			return dailySalesReportMapper.countsometimeMarketingStatistics(depId,rd.getStringMonthStart(),rd.getStringMonthEnd());
		}
		if (TimeType.QTR.getCode()==timeType) {
			return dailySalesReportMapper.countsometimeMarketingStatistics(depId,rd.getStringQuarterStart(),rd.getStringQuarterEnd());
		}
		if (TimeType.YEAR.getCode()==timeType) {
			return dailySalesReportMapper.countsometimeMarketingStatistics(depId,rd.getStringYearStart(),rd.getStringYearEnd());
		}
		if (TimeType.ZS.getCode()==timeType) {
			return dailySalesReportMapper.countsometimeMarketingStatistics(depId,startTime,endTime);
		}
		return null;
	}

	public SomeTimeMarketingBo countSometimeMarketingDepStatistics(String depId, Integer timeType, String startTime,
			String endTime) {

		ReportDate rd = new ReportDate(new Date());
		if (TimeType.WEEKLY.getCode()==timeType) {
			return dailySalesReportMapper.countsometimeMarketingDepStatistics(depId,rd.getStringWeeklyStart(),rd.getStringWeeklyEnd());
		}
		if (TimeType.MONTH.getCode()==timeType) {
			return dailySalesReportMapper.countsometimeMarketingDepStatistics(depId,rd.getStringMonthStart(),rd.getStringMonthEnd());
		}
		if (TimeType.QTR.getCode()==timeType) {
			return dailySalesReportMapper.countsometimeMarketingDepStatistics(depId,rd.getStringQuarterStart(),rd.getStringQuarterEnd());
		}
		if (TimeType.YEAR.getCode()==timeType) {
			return dailySalesReportMapper.countsometimeMarketingDepStatistics(depId,rd.getStringYearStart(),rd.getStringYearEnd());
		}
		if (TimeType.ZS.getCode()==timeType) {
			return dailySalesReportMapper.countsometimeMarketingDepStatistics(depId,startTime,endTime);
		}
			return null;
	}

	public List<technicalDepStatisticsListBo> technicalDepStatistics(String depId, Integer timeType, String startTime, String endTime,
			String publishStatus, String taskStatus) {
		ReportDate rd = new ReportDate(new Date());
		if (TimeType.WEEKLY.getCode()==timeType) {
			return dailySalesReportMapper.technicalDepStatistics(depId,rd.getStringWeeklyStart(),rd.getStringWeeklyEnd(),publishStatus,taskStatus);
		}
		if (TimeType.MONTH.getCode()==timeType) {
			return dailySalesReportMapper.technicalDepStatistics(depId,rd.getStringMonthStart(),rd.getStringMonthEnd(),publishStatus,taskStatus);
		}
		if (TimeType.QTR.getCode()==timeType) {
			return dailySalesReportMapper.technicalDepStatistics(depId,rd.getStringQuarterStart(),rd.getStringQuarterEnd(),publishStatus,taskStatus);
		}
		if (TimeType.YEAR.getCode()==timeType) {
			return dailySalesReportMapper.technicalDepStatistics(depId,rd.getStringYearStart(),rd.getStringYearEnd(),publishStatus,taskStatus);
		}
		if (TimeType.ZS.getCode()==timeType) {
			return dailySalesReportMapper.technicalDepStatistics(depId,startTime,endTime,publishStatus,taskStatus);
		}
		return null;
	}

	public List<userDataListBo> userDataStatistics(String deps) {
		List<String> listDep=new ArrayList<>();
		if (deps!=null){
			listDep=departmentService.parseArray(deps);
		}
		return userMapper.userDataStatistics(listDep);
	}

	public userDataListBo userDataStatisticsCount(String deps) {
		List<String> listDep=new ArrayList<>();
		if (deps!=null){
			listDep=departmentService.parseArray(deps);
		}
		return userMapper.userDataStatisticsCount(listDep);
	}


}
