package com.yuchen.saas.park.service.service.resource.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yuchen.saas.park.api.dto.resource.CancelSubscribeReqDTO;
import com.yuchen.saas.park.api.dto.resource.ResourceSubscribeInfoReqDTO;
import com.yuchen.saas.park.api.dto.resource.ResourceSubscribeSubmitReqDTO;
import com.yuchen.saas.park.api.entity.resource.SaasParkResourceSubscribeBill;
import com.yuchen.saas.park.api.entity.resource.SaasParkResourceSubscribeInfo;
import com.yuchen.saas.park.api.vo.resource.ResourceInfoVO;
import com.yuchen.saas.park.api.vo.resource.ResourceSubscribeBillVO;
import com.yuchen.saas.park.api.vo.resource.ResourceSubscribeInfoVO;
import com.yuchen.saas.park.service.mapper.resource.SaasParkResourceSubscribeInfoMapper;
import com.yuchen.saas.park.service.service.resource.ISaasParkResourceInfoService;
import com.yuchen.saas.park.service.service.resource.ISaasParkResourceSubscribeBillService;
import com.yuchen.saas.park.service.service.resource.ISaasParkResourceSubscribeInfoService;
import com.yuchen.saas.park.service.util.PageConverterUtils;
import org.nest.springwrap.core.log.exception.ServiceException;
import org.nest.springwrap.core.mp.base.BaseServiceImpl;
import org.nest.springwrap.core.mp.support.NestPage;
import org.nest.springwrap.core.secure.ContextUser;
import org.nest.springwrap.core.secure.utils.AuthUtil;
import org.nest.springwrap.core.tool.utils.Func;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 资源预定表 服务实现类
 * </p>
 *
 * @author zhouhanwei
 * @since 2024-10-17
 */
@Service
public class SaasParkResourceSubscribeInfoServiceImpl extends BaseServiceImpl<SaasParkResourceSubscribeInfoMapper, SaasParkResourceSubscribeInfo> implements ISaasParkResourceSubscribeInfoService {

    @Resource
    private ISaasParkResourceInfoService iSaasParkResourceInfoService;

    @Resource
    private ISaasParkResourceSubscribeBillService iSaasParkResourceSubscribeBillService;

    /**
     * 查看是否有资源预定
     * @param resourceId 资源id
     * @return 资源预约数量
     */
    @Override
    public Integer getResourceSubscribeCount(Long resourceId) {
        return count(new LambdaQueryWrapper<SaasParkResourceSubscribeInfo>()
                .eq(SaasParkResourceSubscribeInfo::getResourceId, resourceId)
                .eq(SaasParkResourceSubscribeInfo::getUseState, "0"));
    }

    @Override
    public NestPage<ResourceSubscribeInfoVO> findResourceSubscribeInfoPage(ResourceSubscribeInfoReqDTO reqDTO) {
        Page<ResourceSubscribeInfoVO> page = new Page<>(reqDTO.getCurrent(), reqDTO.getSize());
        page = baseMapper.findResourceSubscribeInfoPage(page, reqDTO);
        return PageConverterUtils.convert(page);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResourceSubscribeBillVO submitResourceSubscribe(ResourceSubscribeSubmitReqDTO reqDTO) {
        ResourceSubscribeBillVO billVO = new ResourceSubscribeBillVO();
        SaasParkResourceSubscribeInfo subscribeInfo = new SaasParkResourceSubscribeInfo();
        ResourceInfoVO resourceInfoVO = iSaasParkResourceInfoService.getResourceInfoById(reqDTO.getResourceId());
        BeanUtil.copyProperties(reqDTO, subscribeInfo);
        // 查询已预定的资源列表
        List<SaasParkResourceSubscribeInfo> subscribeInfoList = list(new LambdaQueryWrapper<SaasParkResourceSubscribeInfo>()
                .eq(SaasParkResourceSubscribeInfo::getResourceId, reqDTO.getResourceId())
                .eq(SaasParkResourceSubscribeInfo::getSubscribeState, "1")
                .in(SaasParkResourceSubscribeInfo::getUseState, "0", "1"));
        // 判断时间有无重叠
        validDateIntersection(resourceInfoVO, subscribeInfo, subscribeInfoList);
        // 判断是否需要收费
        if (StrUtil.equals(resourceInfoVO.getChargeMode(), "2")) {
            billVO.setSubscribeState("1");
            save(subscribeInfo);
        } else {
            billVO.setSubscribeState("0");
            save(subscribeInfo);
            // 计算支付金额
            billVO.setSubscribePrice(resourceInfoVO.getCurrentPrice());
            List<String> subscribeDateList = Arrays.asList(subscribeInfo.getReserveTimeJson().split(",").clone());
            long subscribePayCost = subscribeDateList.size() * billVO.getSubscribePrice();
            billVO.setSubscribePayCost(subscribePayCost);
            // 创建账单
            SaasParkResourceSubscribeBill subscribeBill = new SaasParkResourceSubscribeBill();
            subscribeBill.setResourceSubscribeId(subscribeInfo.getId());
            subscribeBill.setResourceId(resourceInfoVO.getId());
            subscribeBill.setPayStatus(0);
            subscribeBill.setSupposeCost(subscribePayCost);
            iSaasParkResourceSubscribeBillService.save(subscribeBill);
        }
        BeanUtil.copyProperties(subscribeInfo, billVO);
        return billVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean noPaySubscribe(Long subscribeId) {
        ContextUser contextUser = AuthUtil.getUser();
        if (Objects.isNull(contextUser)) {
            throw new ServiceException("用户不存在");
        }
        SaasParkResourceSubscribeInfo subscribeInfo = getById(subscribeId);
        if (Func.isEmpty(subscribeInfo)) {
            throw new ServiceException("预定信息不存在");
        }
        // 更新账单表
        SaasParkResourceSubscribeBill subscribeBill = iSaasParkResourceSubscribeBillService.getOne(new LambdaQueryWrapper<SaasParkResourceSubscribeBill>()
                .eq(SaasParkResourceSubscribeBill::getResourceSubscribeId, subscribeId), false);
        if (Func.isEmpty(subscribeBill)) {
            throw new ServiceException("预定账单信息不存在");
        }
        subscribeBill.setOperatorUserId(contextUser.getUserDetailId());
        subscribeInfo.setUseState("0");
        subscribeInfo.setSubscribeState("1");
        return updateById(subscribeInfo);
    }

    @Override
    public Boolean cancelSubscribe(CancelSubscribeReqDTO reqDTO) {
        ContextUser contextUser = AuthUtil.getUser();
        if (Objects.isNull(contextUser)) {
            throw new ServiceException("用户不存在");
        }
        SaasParkResourceSubscribeInfo subscribeInfo = getById(reqDTO.getSubscribeId());
        if (Func.isEmpty(subscribeInfo)) {
            throw new ServiceException("预定信息不存在");
        }
        subscribeInfo.setCancellerUserId(contextUser.getUserDetailId());
        subscribeInfo.setCancelReason(reqDTO.getCancelReason());
        return updateById(subscribeInfo);
    }

    /**
     * 校验预定时间有无重复
     * @param resourceInfoVO 资源信息
     * @param subscribeInfo 预定信息
     * @param subscribeInfoList 已预定信息
     */
    private void validDateIntersection(ResourceInfoVO resourceInfoVO, SaasParkResourceSubscribeInfo subscribeInfo, List<SaasParkResourceSubscribeInfo> subscribeInfoList) {
        List<String> subscribeDateAllList = new ArrayList<>();
        // 判断当前资源是否按时
        if (StrUtil.equals(resourceInfoVO.getSubscribeType(), "1")) {
            // 整理已预定时间列表
            for (SaasParkResourceSubscribeInfo subscribeDate : subscribeInfoList) {
                List<String> subscribeDateList;
                // 判断之前预约是否也是按时，是的话直接取数组对象内容，不是则计算获取时间内容
                if (StrUtil.equals(subscribeDate.getSubscribeType(), "1")) {
                    subscribeDateList = Arrays.asList(subscribeDate.getReserveTimeJson().split(",").clone());
                    subscribeDateAllList.addAll(subscribeDateList);
                }
                if (StrUtil.equals(subscribeDate.getSubscribeType(), "2")) {
                    // 把天转成每小时
                    LocalDateTime startTime = LocalDateTime.ofInstant(subscribeDate.getReserveStartTime().toInstant(), ZoneId.systemDefault());
                    LocalDateTime endTime = LocalDateTime.ofInstant(subscribeDate.getReserveEndTime().toInstant(), ZoneId.systemDefault());
                    List<String> stringList = getTimePeriod(startTime, endTime);
                    subscribeDateAllList.addAll(stringList);
                }
            }
            LocalDateTime startTime = LocalDateTime.ofInstant(subscribeInfo.getReserveStartTime().toInstant(), ZoneId.systemDefault());
            LocalDateTime endTime = LocalDateTime.ofInstant(subscribeInfo.getReserveEndTime().toInstant(), ZoneId.systemDefault());
            List<String> subscribeList = getTimePeriod(startTime, endTime);
            String reserveTimeJson = String.join(",", subscribeList);
            // 保存时间信息
            subscribeInfo.setReserveTimeJson(reserveTimeJson);
            // 校验时间有无重叠
            if (Func.isEmpty(subscribeDateAllList)) {
                return;
            }
            List<String> intersectionList = subscribeDateAllList.stream()
                    .filter(subscribeList::contains)
                    .collect(Collectors.toList());
            if (Func.isEmpty(intersectionList)) {
                return;
            }
            throw new ServiceException("选择的时间段存在重叠，无法提交");
        }
        // 判断当前资源是否按天
        if (StrUtil.equals(resourceInfoVO.getSubscribeType(), "2")) {
            // 整理已预定时间列表
            for (SaasParkResourceSubscribeInfo subscribeDate : subscribeInfoList) {
                List<String> subscribeDateList;
                // 判断之前预约是否也是按时，是的话直接取数组对象内容，不是则计算获取时间内容
                if (StrUtil.equals(subscribeDate.getSubscribeType(), "2")) {
                    subscribeDateList = Arrays.asList(subscribeDate.getReserveTimeJson().split(",").clone());
                    subscribeDateAllList.addAll(subscribeDateList);
                }
                if (StrUtil.equals(subscribeDate.getSubscribeType(), "1")) {
                    // 把每小时按天取
                    String startTime = DateUtil.format(subscribeDate.getReserveStartTime(), "yyyy-MM-dd");
                    String endTime = DateUtil.format(subscribeDate.getReserveEndTime(), "yyyy-MM-dd");
                    List<String> stringList = findEveryDay(startTime, endTime);
                    subscribeDateAllList.addAll(stringList);
                }
            }
            String startTime = DateUtil.format(subscribeInfo.getReserveStartTime(), "yyyy-MM-dd");
            String endTime = DateUtil.format(subscribeInfo.getReserveEndTime(), "yyyy-MM-dd");
            List<String> subscribeList = findEveryDay(startTime, endTime);
            String reserveTimeJson = String.join(",", subscribeList);
            subscribeInfo.setReserveTimeJson(reserveTimeJson);
            // 校验时间有无重叠
            if (Func.isEmpty(subscribeDateAllList)) {
                return;
            }
            List<String> intersectionList = subscribeDateAllList.stream()
                    .filter(subscribeList::contains)
                    .collect(Collectors.toList());
            if (Func.isEmpty(intersectionList)) {
                return;
            }
            throw new ServiceException("选择的时间段存在重叠，无法提交");
        }

    }

    /**
     * 获取两个时间之间的所有时间段（每15分钟一个时间段）
     */
    public static List<String> getTimePeriod(LocalDateTime startTime, LocalDateTime endTime){
        List<String> result = new LinkedList<>();
        LocalDateTime currentTime = startTime;
        while (!currentTime.isAfter(endTime)) {
            // 改成每60分钟一个时间段
            if (currentTime.getMinute() % 60 == 0) {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                // 使用format方法将LocalDateTime转换为字符串
                String formattedDateTime = currentTime.format(formatter);
                result.add(formattedDateTime);
            }
            currentTime = currentTime.plusMinutes(60);
        }
        return result;
    }

    /**
     * 获取两个时间之间的所有日期
     */
    public static List<String> findEveryDay(String beginTime, String endTime) {
        LocalDate startDate = LocalDate.parse(beginTime);
        LocalDate endDate = LocalDate.parse(endTime);
        List<String> dateList = new ArrayList<>();
        while (!startDate.isAfter(endDate)) {
            dateList.add(startDate.toString());
            startDate = startDate.plusDays(1);
        }
        return dateList;
    }
}
