package cn.ey.biz.module.system.service.category.dynamic.service;

import cn.ey.biz.common.enums.CommonStatusEnum;
import cn.ey.biz.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.ey.biz.module.system.api.category.dto.CategoryDynamicReq;
import cn.ey.biz.module.system.dal.dataobject.category.DictCalendarDO;
import cn.ey.biz.module.system.dal.dataobject.category.DictDayDO;
import cn.ey.biz.module.system.dal.dataobject.category.DictPublicHolidayRefDO;
import cn.ey.biz.module.system.dal.mapper.category.DictCalendarMapper;
import cn.ey.biz.module.system.dal.mapper.category.DictDayMapper;
import cn.ey.biz.module.system.dal.mapper.category.DictPublicHolidayRefMapper;
import cn.ey.biz.module.system.enums.ErrorCodeConstants;
import cn.ey.biz.module.system.enums.YesOrNoStatusEnum;
import cn.ey.biz.module.system.enums.category.CategoryCodeEnum;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.ey.biz.common.exception.util.ServiceExceptionUtil.exception;


@Service(DictCalenderServiceImpl.CATEGORY_CODE + IBaseService.BASE_SERVICE_NAME)
public class DictCalenderServiceImpl extends ServiceImpl<DictCalendarMapper, DictCalendarDO> implements IBaseService {

    public static final String CATEGORY_CODE = "Calendar";

    @Resource
    private DictCalendarMapper dictCalendarMapper;
    @Resource
    private DictDayMapper dictDayMapper;
    @Resource
    private DictPublicHolidayRefMapper dictPublicHolidayRefMapper;

    @Override
    public List<Map<String, Object>> get(HttpServletRequest request, HttpServletResponse response, CategoryDynamicReq req) {
        return dictCalendarMapper.selectMaps(null);
    }

    /**
     * 获取周期时间内工作日的日历数据  requestBody (key - geoRegion beginDate ，endDate)
     *
     * @param req 请求参数
     * @return 周期时间内工作日的日历数据
     */
    public List<Map<String, Object>> getWorkDayByPeriod(HttpServletRequest request, HttpServletResponse response, CategoryDynamicReq req) {
        Map<String, Object> params = checkParams(CategoryCodeEnum.FunctionTypeConstants.DICT_CALENDAR_WORK_DAY_BY_PERIOD, req);
        return dictCalendarMapper.selectMaps(new LambdaQueryWrapperX<DictCalendarDO>()
                .eqIfPresent(DictCalendarDO::getCountryRegionCode, params.get(DictCalenderCustomParam.COUNTRY_REGION.getValue()))
                .eqIfPresent(DictCalendarDO::getWorkday, params.get(DictCalenderCustomParam.WORK_DAY.getValue()))
                .betweenIfPresent(DictCalendarDO::getDayDate,
                        params.get(DictCalenderCustomParam.BEGIN_DATE.getValue()),
                        params.get(DictCalenderCustomParam.END_DATE.getValue())));
    }


    /**
     * 获取周期时间内工作日的日历数据  requestBody (key - geoRegion， FiscalYear)
     *
     * @param req 请求参数
     * @return 周期时间内工作日的日历数据
     */
    @SuppressWarnings("unchecked")
    @Transactional(rollbackFor = Exception.class)
    public List<Map<String, Object>> getInitCalendar(HttpServletRequest request, HttpServletResponse response, CategoryDynamicReq req) {
        Map<String, Object> params = checkParams(CategoryCodeEnum.FunctionTypeConstants.DICT_DAY_INIT_CALENDAR_DATA, req);
        String fiscalYear = (String) params.get(DictCalenderCustomParam.FISCAL_YEAR.getValue());
        String countryRegionCode = (String) params.get(DictCalenderCustomParam.COUNTRY_REGION.getValue());
        List<LocalDate> specialWorkDate = (List<LocalDate>) params.get(DictCalenderCustomParam.SPECIAL_WORK_DATE.getValue());
        // 财天数据判断
        List<DictDayDO> dictDayEntities = dictDayMapper.selectList(new LambdaUpdateWrapper<DictDayDO>()
                .eq(DictDayDO::getFiscalYear, fiscalYear));

        if (CollectionUtil.isEmpty(dictDayEntities)) {
            throw exception(ErrorCodeConstants.CATEGORY_DATA_REQUEST_BODY_PARAMETER_ERROR, "财天数据");
        }
        // 假期数据获取
        List<DictPublicHolidayRefDO> dictPublicHolidayRefEntities = dictPublicHolidayRefMapper.selectList(new LambdaUpdateWrapper<DictPublicHolidayRefDO>()
                .eq(DictPublicHolidayRefDO::getCountryRegionCode, countryRegionCode)
                .eq(DictPublicHolidayRefDO::getNewVersion, YesOrNoStatusEnum.YES.getCode()));
        if (CollectionUtil.isEmpty(dictPublicHolidayRefEntities)) {
            throw exception(ErrorCodeConstants.CATEGORY_DATA_REQUEST_BODY_PARAMETER_ERROR, "假期数据");
        }

        dictCalendarMapper.deleteByFiscalYear(countryRegionCode, fiscalYear);
        List<DictCalendarDO> dictCalendarEntities = new ArrayList<>();
        for (DictDayDO dictDayDO : dictDayEntities) {
            DictCalendarDO dictCalendarDO = new DictCalendarDO();
            setDayInfo(dictDayDO.getDayDate(), dictPublicHolidayRefEntities, specialWorkDate, dictCalendarDO);
            dictCalendarDO.setCountryRegionCode(countryRegionCode);
            dictCalendarDO.setDayId(dictDayDO.getDayId());
            dictCalendarDO.setDayDate(dictDayDO.getDayDate());
            dictCalendarDO.setStatus(CommonStatusEnum.ENABLE.getStatus());
            dictCalendarDO.setCreator("1");
            dictCalendarDO.setUpdater("1");
            dictCalendarEntities.add(dictCalendarDO);
        }
        if (CollectionUtil.isNotEmpty(dictCalendarEntities)) {
            this.saveBatch(dictCalendarEntities);
        }
        return null;
    }

    private void setDayInfo(LocalDate dayDate, List<DictPublicHolidayRefDO> dictPublicHolidayRefData, List<LocalDate> specialWorkDate, DictCalendarDO dictCalendarDO) {
        int workDay = YesOrNoStatusEnum.YES.getCode();
        int holidayLegal = YesOrNoStatusEnum.NO.getCode();
        int holidayOvertime = YesOrNoStatusEnum.NO.getCode();

        DictPublicHolidayRefDO dictPublicHolidayRefDO = dictPublicHolidayRefData.stream().filter(holiday -> dayDate.compareTo(holiday.getStartDate()) >= 0
                && dayDate.compareTo(holiday.getEndDate()) <= 0).findFirst().orElse(null);
        if (dictPublicHolidayRefDO == null) {
            // 判断调休
            if (specialWorkDate != null && specialWorkDate.contains(dayDate)) {
                holidayOvertime = YesOrNoStatusEnum.YES.getCode();
            } else {
                // 不调休判断weekend
                workDay = dayDate.getDayOfWeek() == DayOfWeek.SATURDAY || dayDate.getDayOfWeek() == DayOfWeek.SUNDAY
                        ? YesOrNoStatusEnum.NO.getCode() : YesOrNoStatusEnum.YES.getCode();
            }
        } else {
            workDay = YesOrNoStatusEnum.NO.getCode();
            holidayLegal = YesOrNoStatusEnum.YES.getCode();
            dictCalendarDO.setHolidayCode(dictPublicHolidayRefDO.getHolidayCode());
            dictCalendarDO.setHolidayName(dictPublicHolidayRefDO.getHolidayName());
            dictCalendarDO.setHolidayValue(dictPublicHolidayRefDO.getHolidayValue());
        }
        dictCalendarDO.setWorkday(workDay);
        dictCalendarDO.setHolidayOvertime(holidayOvertime);
        dictCalendarDO.setHolidayLegal(holidayLegal);
    }

    enum DictCalenderCustomParam {

        /**
         * country region String
         */
        COUNTRY_REGION("countryRegionCode"),
        /**
         * 查询日期  String
         */
        BEGIN_DATE("beginDate"),
        END_DATE("endDate"),

        /**
         * 是否工作日
         */
        WORK_DAY("workDay"),

        /**
         * 财年 String
         */
        FISCAL_YEAR("fiscalYear"),

        /**
         * 特殊工作日（假日串休） List
         */
        SPECIAL_WORK_DATE("specialWorkDate");

        final String value;

        DictCalenderCustomParam(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public Map<String, Object> checkParams(String functionType, CategoryDynamicReq req) {
        boolean nullError = false;
        boolean otherError = false;
        StringBuilder errorMsg = new StringBuilder();
        Map<String, Object> result = req.getRequestBody();
        if (CategoryCodeEnum.FunctionTypeConstants.DICT_CALENDAR_WORK_DAY_BY_PERIOD.equals(functionType)) {
            nullError = (result == null
                    || result.get(DictCalenderCustomParam.BEGIN_DATE.getValue()) == null
                    || result.get(DictCalenderCustomParam.END_DATE.getValue()) == null
                    || result.get(DictCalenderCustomParam.COUNTRY_REGION.getValue()) == null
                    || result.get(DictCalenderCustomParam.WORK_DAY.getValue()) == null);
            if (!nullError) {
                try {
                    String beginDate = (String) result.get(DictCalenderCustomParam.BEGIN_DATE.getValue());
                    String endDate = (String) result.get(DictCalenderCustomParam.END_DATE.getValue());
                    LocalDate begin = LocalDateTimeUtil.parseDate(beginDate, "yyyy-MM-dd");
                    LocalDate end = LocalDateTimeUtil.parseDate(endDate, "yyyy-MM-dd");
                    result.put(DictCalenderCustomParam.BEGIN_DATE.getValue(), begin);
                    result.put(DictCalenderCustomParam.END_DATE.getValue(), end);
                } catch (Exception e) {
                    otherError = true;
                }
            }
            if (nullError || otherError) {
                errorMsg.append(DictCalenderCustomParam.COUNTRY_REGION.getValue()).append(",")
                        .append(DictCalenderCustomParam.BEGIN_DATE.getValue()).append(",")
                        .append(DictCalenderCustomParam.END_DATE.getValue()).append(",")
                        .append(DictCalenderCustomParam.WORK_DAY.getValue());
            }
        } else if (CategoryCodeEnum.FunctionTypeConstants.DICT_DAY_INIT_CALENDAR_DATA.equals(functionType)) {
            nullError = (result == null || result.get(DictCalenderCustomParam.FISCAL_YEAR.getValue()) == null
                    || result.get(DictCalenderCustomParam.COUNTRY_REGION.getValue()) == null);
            if (!nullError) {
                try {
                    List<LocalDate> specialWorkDate = null;
                    if (result.get(DictCalenderCustomParam.SPECIAL_WORK_DATE.getValue()) != null) {
                        List<String> specialWorkDateList = (List<String>) result.get(DictCalenderCustomParam.SPECIAL_WORK_DATE.getValue());
                        specialWorkDate = specialWorkDateList.stream().map(item -> LocalDateTimeUtil.parseDate(item, "yyyy-MM-dd")).collect(Collectors.toList());
                    }
                    result.put(DictCalenderCustomParam.SPECIAL_WORK_DATE.getValue(), specialWorkDate);
                } catch (Exception e) {
                    otherError = true;
                }
            }
            if (nullError || otherError) {
                errorMsg.append(DictCalenderCustomParam.COUNTRY_REGION.getValue()).append(",")
                        .append(DictCalenderCustomParam.FISCAL_YEAR.getValue());
            }
        }
        if (nullError || otherError) {
            throw exception(ErrorCodeConstants.CATEGORY_DATA_REQUEST_BODY_PARAMETER_ERROR, errorMsg.toString());
        }
        return result;
    }

}

