package cn.iocoder.yudao.module.member.service.business;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.member.controller.admin.business.vo.BusinessPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.business.vo.BusinessRespVO;
import cn.iocoder.yudao.module.member.controller.admin.business.vo.BusinessSaveReqVO;
import cn.iocoder.yudao.module.member.controller.admin.business.vo.StatisticsVo;
import cn.iocoder.yudao.module.member.controller.admin.screen.vo.TurnoverVO;
import cn.iocoder.yudao.module.member.controller.app.business.vo.BusinessDataVO;
import cn.iocoder.yudao.module.member.dal.dataobject.business.BusinessDO;
import cn.iocoder.yudao.module.member.dal.dataobject.orderdetail.OrderDetailDO;
import cn.iocoder.yudao.module.member.dal.mysql.business.BusinessMapper;
import cn.iocoder.yudao.module.member.dal.mysql.card.CardMapper;
import cn.iocoder.yudao.module.member.dal.mysql.order.DishOrderMapper;
import cn.iocoder.yudao.module.member.dal.mysql.orderdetail.OrderDetailMapper;
import cn.iocoder.yudao.module.member.enums.CostTypeEnum;
import cn.iocoder.yudao.module.member.enums.TimePeriodEnum;
import cn.iocoder.yudao.module.member.service.orderdetail.OrderDetailService;
import cn.iocoder.yudao.module.member.util.MemberTimeUtils;
import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;


import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.member.service.amount.LockManager.getStoreLock;

/**
 * 营业统计 Service 实现类
 *
 * @author 开发账号
 */
@Service
@Validated
public class BusinessServiceImpl implements BusinessService {

    @Resource
    private BusinessMapper businessMapper;
    @Resource
    private DishOrderMapper dishOrderMapper;
    @Resource
    private OrderDetailMapper orderDetailMapper;
    @Resource
    private CardMapper cardMapper;

    @Override
    public Long createBusiness(BusinessSaveReqVO createReqVO) {
        // 插入
        BusinessDO business = BeanUtils.toBean(createReqVO, BusinessDO.class);
        businessMapper.insert(business);
        // 返回
        return business.getId();
    }

    @Override
    public void updateBusiness(BusinessSaveReqVO updateReqVO) {
        // 校验存在
        validateBusinessExists(updateReqVO.getId());
        // 更新
        BusinessDO updateObj = BeanUtils.toBean(updateReqVO, BusinessDO.class);
        businessMapper.updateById(updateObj);
    }

    @Override
    public void deleteBusiness(Long id) {
        // 校验存在
        validateBusinessExists(id);
        // 删除
        businessMapper.deleteById(id);
    }

    private void validateBusinessExists(Long id) {
        if (businessMapper.selectById(id) == null) {
            throw exception(ErrorCodeConstants.BUSINESS_NOT_EXISTS);
        }
    }

    @Override
    public BusinessDO getBusiness(Long id) {
        return businessMapper.selectById(id);
    }

    @Override
    public PageResult<BusinessDO> getBusinessPage(BusinessPageReqVO pageReqVO) {
        return businessMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional
    public void updateStatistics(StatisticsVo vo) {

        LocalDate now = LocalDate.now();
        if (vo.getTime() != null) {
            now = vo.getTime().toLocalDate();
        }
        // 获取今天的开始时间
        LocalDateTime startOfDay = now.atStartOfDay();
        // 获取今天的结束时间 (23:59:59.999)
        LocalDateTime endOfDay = LocalDateTime.of(now, LocalTime.MAX);

        BusinessDO businessDO = businessMapper.getTodayBusiness(vo.getCarteenId(), startOfDay, endOfDay);
        if (ObjectUtil.isNotEmpty(businessDO)) {
            //重量
            if (ObjectUtil.isNotEmpty(vo.getOrderId())) {
                List<OrderDetailDO> orderDetailDOS = orderDetailMapper.selectList(Wrappers.<OrderDetailDO>lambdaQuery().eq(OrderDetailDO::getOrderId, vo.getOrderId()));
                if (CollectionUtil.isNotEmpty(orderDetailDOS)) {
                    BigDecimal reduce = orderDetailDOS.stream().map(OrderDetailDO::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
                    businessDO.setWeigh(businessDO.getWeigh().add(reduce));
                }
            }
            //订单数
            businessDO.setOrderSum(businessDO.getOrderSum() + vo.getOrderSum());

            //营业额
            if (ObjectUtil.isNotEmpty(vo.getTotalMoney())) {
                businessDO.setTurnover(businessDO.getTurnover().add(vo.getTotalMoney()));
            }

            //客单价
            if (businessDO.getOrderSum() != 0) {
                businessDO.setPriceAvg(businessDO.getTurnover().divide(new BigDecimal(businessDO.getOrderSum()), 2, RoundingMode.HALF_UP));
            } else {
                businessDO.setPriceAvg(BigDecimal.ZERO);
            }

            //减免金额
            if (ObjectUtil.isNotEmpty(vo.getReduceMoney())) {
                businessDO.setReduce(businessDO.getReduce().add(vo.getReduceMoney()));
            }

            //早中晚金额
            if (ObjectUtil.isNotEmpty(vo.getTime())) {
                String timePeriod = TimePeriodEnum.getTimePeriod(vo.getTime());

                if (timePeriod.equals(CostTypeEnum.MORNING.getCode())) {
                    businessDO.setBreakfast(businessDO.getBreakfast().add(vo.getTotalMoney()));
                    businessDO.setBreakfastNum(businessDO.getBreakfastNum() + vo.getOrderSum());
                } else if (timePeriod.equals(CostTypeEnum.NOON.getCode())) {
                    businessDO.setLunch(businessDO.getLunch().add(vo.getTotalMoney()));
                    businessDO.setLunchNum(businessDO.getLunchNum() + vo.getOrderSum());
                } else if (timePeriod.equals(CostTypeEnum.NIGHT.getCode())) {
                    businessDO.setDinner(businessDO.getDinner().add(vo.getTotalMoney()));
                    businessDO.setDinnerNum(businessDO.getDinnerNum() + vo.getOrderSum());
                }
            }

            //补贴金额
            if (ObjectUtil.isNotEmpty(vo.getSubsidyPurse())) {
                businessDO.setSubsidyPurse(businessDO.getSubsidyPurse().add(vo.getSubsidyPurse()));
            }

            businessMapper.updateById(businessDO);
        }


    }

    @Override
    public BusinessDataVO getDay(String time, Long carteenId) {

        Integer nowDay = MemberTimeUtils.getDay(time);
        time = MemberTimeUtils.getYearMonth(time);

        BusinessDataVO businessDataVO = new BusinessDataVO();

        LambdaQueryWrapper<BusinessDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BusinessDO::getCarteenId, carteenId);
        wrapper.orderByAsc(BusinessDO::getCreateTime);
        wrapper.apply("DATE_FORMAT(create_time, '%Y-%m') = {0}", time);
        List<BusinessDO> businessDOS = businessMapper.selectList(wrapper);

        Map<Integer, BusinessDO> map = businessDOS.stream().collect(Collectors.toMap(
                vo -> vo.getCreateTime().toLocalDate().getDayOfMonth()
                , vo -> vo, (existing, replacement) -> existing));

        // 创建一个Map来存储每天的数据
        Map<Integer, BusinessRespVO> monthData = new LinkedHashMap<>();


        // 定义日期格式化器
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");

        // 将字符串解析为 YearMonth 对象
        YearMonth yearMonth = YearMonth.parse(time, formatter);

        // 获取当月的总天数
        int daysInMonth = yearMonth.lengthOfMonth();

        //获取上一个月最后一天的数据
        // 获取上一个月的最后一天
        YearMonth previousMonth = yearMonth.minusMonths(1);
        LocalDate lastDayOfPreviousMonth = previousMonth.atEndOfMonth();
        // 定义格式化器，将 LocalDate 转换为 yyyy-MM-dd 格式
        DateTimeFormatter formatterDay = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        // 将 LocalDate 转换为指定格式的字符串
        String formattedDate = lastDayOfPreviousMonth.format(formatterDay);


        LambdaQueryWrapper<BusinessDO> previousWrapper = new LambdaQueryWrapper<>();
        previousWrapper.eq(BusinessDO::getCarteenId, carteenId);
        previousWrapper.orderByAsc(BusinessDO::getCreateTime);
        previousWrapper.apply("DATE_FORMAT(create_time, '%Y-%m-%d') = {0}", formattedDate);
        List<BusinessDO> previousList = businessMapper.selectList(previousWrapper);
        BusinessRespVO old = new BusinessRespVO();
        if (CollectionUtil.isNotEmpty(previousList)) {
            BeanUtil.copyProperties(previousList.get(0), old);
        } else {
            old = getInitBusinessRespVO();
        }

        // 循环遍历该月的每一天
        for (int day = 1; day <= daysInMonth; day++) {
            BusinessRespVO bean = new BusinessRespVO();
            if (map.get(day) == null) {
                bean = getInitBusinessRespVO().setTime(String.valueOf(day));
            } else {
                bean = BeanUtils.toBean(map.get(day), BusinessRespVO.class);
                bean.setTime(String.valueOf(day));
            }
            //计算差额
            if (day == 1) {
                compare(bean, old);
            } else {
                compare(bean, monthData.get(day - 1));
            }
            monthData.put(day, bean);

        }
        businessDataVO.setNow(monthData.get(nowDay));
        businessDataVO.setBusinessList(new ArrayList<>(monthData.values()));
        return businessDataVO;
    }

    @Override
    public BusinessDataVO getWeek(String time, Long carteenId) {

        BusinessDataVO businessDataVO = new BusinessDataVO();

        LambdaQueryWrapper<BusinessDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BusinessDO::getCarteenId, carteenId);
        wrapper.orderByAsc(BusinessDO::getCreateTime);
        wrapper.apply("DATE_FORMAT(create_time, '%Y-%m') = {0}", time);
        List<BusinessDO> businessDOS = businessMapper.selectList(wrapper);

        Map<Integer, BusinessRespVO> map = new HashMap<>();

        int totalWeeksOfMonth = MemberTimeUtils.getTotalWeeksOfMonth(time);
        for (int i = 1; i <= totalWeeksOfMonth; i++) {
            BusinessRespVO bean = getInitBusinessRespVO().setTime(String.valueOf(i));
            map.put(i, bean);
        }

        for (BusinessDO businessDO : businessDOS) {

            LocalDate date = businessDO.getCreateTime().toLocalDate();

            // 获取区域的周定义（可以是ISO标准或其他区域标准）
            WeekFields weekFields = WeekFields.of(Locale.getDefault());

            // 使用weekFields获取当前日期属于当月的第几周
            int i = date.get(weekFields.weekOfMonth());
            BusinessRespVO businessRespVO = map.get(i);
            handleBusiness(businessDO, businessRespVO);
        }


        businessDataVO.setBusinessList(new ArrayList<>(map.values()));
        return businessDataVO;
    }

    @Override
    public BusinessDataVO getMonth(String time, Long carteenId) {
        time = MemberTimeUtils.getYearMonth(time);

        BusinessDataVO businessDataVO = new BusinessDataVO();

        int year = MemberTimeUtils.getYearFromYearMonthString(time);

        LambdaQueryWrapper<BusinessDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BusinessDO::getCarteenId, carteenId);
        wrapper.orderByAsc(BusinessDO::getCreateTime);
        wrapper.apply("DATE_FORMAT(create_time, '%Y') = {0}", year);
        List<BusinessDO> businessDOS = businessMapper.selectList(wrapper);

        Map<Integer, BusinessRespVO> map = new HashMap<>();
        //时间处理
        for (int i = 1; i <= 12; i++) {
            BusinessRespVO bean = getInitBusinessRespVO();
            bean.setTime(String.valueOf(i));
            map.put(i, bean);
        }
        //数据统计
        for (BusinessDO businessDO : businessDOS) {

            LocalDate date = businessDO.getCreateTime().toLocalDate();

            // 当前日期属于哪月
            int i = date.getMonthValue();
            BusinessRespVO businessRespVO = map.get(i);
            handleBusiness(businessDO, businessRespVO);
        }

        //上一年最后一月
        int previousYear = year - 1;
        LambdaQueryWrapper<BusinessDO> previousWrapper = new LambdaQueryWrapper<>();
        previousWrapper.eq(BusinessDO::getCarteenId, carteenId);
        previousWrapper.orderByAsc(BusinessDO::getCreateTime);
        previousWrapper.apply("DATE_FORMAT(create_time, '%Y-%m') = {0}", previousYear + "-12");
        List<BusinessDO> previousList = businessMapper.selectList(previousWrapper);
        BusinessRespVO previousVO = getInitBusinessRespVO();
        for (BusinessDO businessDO : previousList) {
            handleBusiness(businessDO, previousVO);
        }
        //差额统计
        for (int i = 1; i <= 12; i++) {
            if (i == 1) {
                compare(map.get(i), previousVO);
            } else {
                compare(map.get(i), map.get(i - 1));
            }
        }

        int month = MemberTimeUtils.getMonthFromYearMonthString(time);
        businessDataVO.setNow(map.get(month));
        businessDataVO.setBusinessList(new ArrayList<>(map.values()));

        return businessDataVO;
    }


    void handleBusiness(BusinessDO businessDO, BusinessRespVO businessRespVO) {
        //营业额
        businessRespVO.setTurnover(businessRespVO.getTurnover().add(businessDO.getTurnover()));
        //订单数
        businessRespVO.setOrderSum(businessRespVO.getOrderSum() + businessDO.getOrderSum());
        //均单价
        if (businessRespVO.getOrderSum() != 0) {
            businessRespVO.setPriceAvg(businessRespVO.getTurnover().divide(new BigDecimal(businessRespVO.getOrderSum().toString()), 2, RoundingMode.HALF_UP));
        }
        //减免金额
        businessRespVO.setReduce(businessRespVO.getReduce().add(businessDO.getReduce()));
        //重量
        businessRespVO.setWeigh(businessRespVO.getWeigh().add(businessDO.getWeigh()));
        //早
        businessRespVO.setBreakfast(businessRespVO.getBreakfast().add(businessDO.getBreakfast()));
        businessRespVO.setBreakfastNum(businessRespVO.getBreakfastNum() + businessDO.getBreakfastNum());
        //中
        businessRespVO.setLunch(businessRespVO.getLunch().add(businessDO.getLunch()));
        businessRespVO.setLunchNum(businessRespVO.getLunchNum() + businessDO.getLunchNum());
        //晚
        businessRespVO.setDinner(businessRespVO.getDinner().add(businessDO.getDinner()));
        businessRespVO.setDinnerNum(businessRespVO.getDinnerNum() + businessDO.getDinnerNum());
        //门店
        businessRespVO.setCarteenId(businessDO.getCarteenId());
    }

    void compare(BusinessRespVO today, BusinessRespVO last) {
        today.setTurnoverCompare(today.getTurnover().subtract(last.getTurnover()));
        today.setOrderSumCompare(today.getOrderSum() - last.getOrderSum());
        today.setPriceAvgCompare(today.getPriceAvg().subtract(last.getPriceAvg()));
    }

    BusinessRespVO getInitBusinessRespVO() {
        BusinessRespVO previousVO = new BusinessRespVO();
        previousVO.setTurnover(BigDecimal.ZERO).setOrderSum(0).setReduce(BigDecimal.ZERO).setWeigh(BigDecimal.ZERO)
                .setBreakfast(BigDecimal.ZERO).setLunch(BigDecimal.ZERO).setDinner(BigDecimal.ZERO).setPriceAvg(BigDecimal.ZERO)
                .setBreakfastNum(0).setLunchNum(0).setDinnerNum(0);
        return previousVO;
    }

    @Override
    public BusinessDO getToday(Long carteenId) {
        // 获取今天的开始时间
        LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
        // 获取今天的结束时间 (23:59:59.999)
        LocalDateTime endOfDay = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);

        List<BusinessDO> businessDOS = businessMapper.selectList(Wrappers.<BusinessDO>lambdaQuery()
                .between(BusinessDO::getCreateTime, startOfDay, endOfDay)
                .eq(BusinessDO::getCarteenId, carteenId));
        return CollectionUtil.isNotEmpty(businessDOS) ? businessDOS.get(0) : new BusinessDO();
    }

    @Override
    public BusinessRespVO getTurnover(TurnoverVO turnoverVO) {

        List<BusinessDO> businessDOS = businessMapper.selectList(Wrappers.<BusinessDO>lambdaQuery()
                .between(BusinessDO::getCreateTime, MemberTimeUtils.string2Time(turnoverVO.getStartTime()), MemberTimeUtils.string2Time(turnoverVO.getEndTime()))
                .eq(BusinessDO::getCarteenId, turnoverVO.getStoreId()));
        BusinessRespVO businessDO = getInitBusinessRespVO();
        if (CollectionUtil.isNotEmpty(businessDOS)) {
            businessDO.setTurnover(businessDOS.stream().map(BusinessDO::getTurnover).reduce(BigDecimal.ZERO, BigDecimal::add));
            businessDO.setOrderSum(businessDOS.stream().map(BusinessDO::getOrderSum).reduce(0, Integer::sum));
            businessDO.setCustomerSum(businessDOS.stream().map(BusinessDO::getCustomerSum).reduce(0, Integer::sum));
            businessDO.setPriceAvg(businessDOS.stream().map(BusinessDO::getPriceAvg).reduce(BigDecimal.ZERO, BigDecimal::add));
            businessDO.setBreakfast(businessDOS.stream().map(BusinessDO::getBreakfast).reduce(BigDecimal.ZERO, BigDecimal::add));
            businessDO.setLunch(businessDOS.stream().map(BusinessDO::getLunch).reduce(BigDecimal.ZERO, BigDecimal::add));
            businessDO.setDinner(businessDOS.stream().map(BusinessDO::getDinner).reduce(BigDecimal.ZERO, BigDecimal::add));
            businessDO.setBreakfastNum(businessDOS.stream().map(BusinessDO::getBreakfastNum).reduce(0, Integer::sum));
            businessDO.setLunchNum(businessDOS.stream().map(BusinessDO::getLunchNum).reduce(0, Integer::sum));
            businessDO.setDinnerNum(businessDOS.stream().map(BusinessDO::getDinnerNum).reduce(0, Integer::sum));
        }
        return businessDO;
    }

    @Override
    public List<BusinessRespVO> getTurnoverList(TurnoverVO turnoverVO) {
        LocalDateTime startTime = MemberTimeUtils.string2Time(turnoverVO.getStartTime());
        LocalDateTime endTime = MemberTimeUtils.string2Time(turnoverVO.getEndTime());

        LocalDate startDate = startTime.toLocalDate();
        LocalDate endDate = endTime.toLocalDate();
        if (startDate.equals(endDate)) {
            // 获取本月开始时间：本月的第一天00:00:00
            startDate = startTime.with(TemporalAdjusters.firstDayOfMonth()).toLocalDate();
            startTime = startDate.atStartOfDay();

            // 获取本月结束时间：本月的最后一天23:59:59.999999999
            endDate = endTime.with(TemporalAdjusters.lastDayOfMonth()).toLocalDate();
            endTime = endDate.atTime(LocalTime.MAX);
        }
        List<BusinessDO> businessDOS = businessMapper.selectList(Wrappers.<BusinessDO>lambdaQuery()
                .between(BusinessDO::getCreateTime, startTime, endTime)
                .eq(BusinessDO::getCarteenId, turnoverVO.getStoreId()));

        Map<LocalDateTime, BusinessDO> map = businessDOS.stream()
                .filter(Objects::nonNull) // 过滤掉null值
                .collect(Collectors.toMap(
                        vo -> vo.getCreateTime().toLocalDate().atStartOfDay(),
                        vo -> vo,
                        (existing, replacement) -> existing
                ));
        // 生成从 startDate 到 endDate 的完整日期列表
        List<LocalDateTime> completeDates = new ArrayList<>();
        for (LocalDateTime date = startTime.toLocalDate().atStartOfDay(); !date.isAfter(endTime.toLocalDate().atStartOfDay()); date = date.plus(1, ChronoUnit.DAYS)) {
            completeDates.add(date);
        }
        // 遍历完整日期列表，补齐缺失的数据
        List<BusinessRespVO> completeRecords = new ArrayList<>();
        for (LocalDateTime date : completeDates) {
            BusinessRespVO bean;
            if (map.get(date) == null) {
                bean = getInitBusinessRespVO().setTime(MemberTimeUtils.Time2String(date));
            } else {
                bean = BeanUtils.toBean(map.get(date), BusinessRespVO.class);
                bean.setTime(MemberTimeUtils.Time2String(date));
            }
            completeRecords.add(bean);
        }
        return completeRecords;
    }
}