package com.red.circle.wallet.infra.database.rds.service.diamond.impl;

import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.wallet.infra.database.rds.dao.SalaryDiamondRunningWaterDAO;
import com.red.circle.wallet.infra.database.rds.entity.diamond.DiamondRunningWater;
import com.red.circle.wallet.infra.database.rds.entity.diamond.SalaryDiamondRunningWater;
import com.red.circle.wallet.infra.database.rds.service.diamond.SalaryDiamondRunningWaterService;
import com.red.circle.wallet.inner.model.cmd.UserSalaryDiamondRunningWaterQryCmd;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 用户工资钻石流水记录 服务实现类.
 * </p>
 *
 * @author zongpubin on 2024-03-13 17:24
 */
@Service
@RequiredArgsConstructor
public class SalaryDiamondRunningWaterServiceImpl extends
    BaseServiceImpl<SalaryDiamondRunningWaterDAO, SalaryDiamondRunningWater> implements
    SalaryDiamondRunningWaterService {

  @Override
  public List<SalaryDiamondRunningWater> listUserLatestOneMonths(Long userId, Integer type, Integer size, Long lastId) {
    return query()
            .eq(Objects.nonNull(type), SalaryDiamondRunningWater::getType, type)
            .eq(Objects.nonNull(userId), SalaryDiamondRunningWater::getUserId, userId)
            .lt(Objects.nonNull(lastId), SalaryDiamondRunningWater::getId, lastId)
            .orderByDesc(SalaryDiamondRunningWater::getCreateTime)
            .last(PageConstant.formatLimit(size))
            .list();
  }

  @Override
  public PageResult<SalaryDiamondRunningWater> runningWaterPage(
      UserSalaryDiamondRunningWaterQryCmd query) {
    return query()
        .eq(Objects.nonNull(query.getUserId()), SalaryDiamondRunningWater::getUserId,
            query.getUserId())
        .eq(Objects.nonNull(query.getType()), SalaryDiamondRunningWater::getType, query.getType())
        .eq(StringUtils.isNotBlank(query.getSysOrigin()), SalaryDiamondRunningWater::getSysOrigin,
            query.getSysOrigin())
        .eq(Objects.nonNull(query.getEvent()), SalaryDiamondRunningWater::getSalaryEvent, query.getEvent())
        .gt(Objects.nonNull(query.getStartTime()), SalaryDiamondRunningWater::getCreateTime, query.startTimeToLocalDateTime())
        .lt(Objects.nonNull(query.getEndTime()), SalaryDiamondRunningWater::getCreateTime, query.endTimeToLocalDateTime())
        .orderByDesc(SalaryDiamondRunningWater::getCreateTime)
        .page(query.getPageQuery());
  }

  @Override
  public List<SalaryDiamondRunningWater> getTeamIdRunWater(
          Long teamId, Integer billBelong) {
    return query()
            .eq(Objects.nonNull(teamId), SalaryDiamondRunningWater::getTeamId, teamId)
            .eq(Objects.nonNull(billBelong), SalaryDiamondRunningWater::getBillBelong, billBelong)
            .list();
  }

  @Override
  public List<SalaryDiamondRunningWater> appPage(Long userId, Integer size, Long lastId) {
    return query()
            .eq(Objects.nonNull(userId), SalaryDiamondRunningWater::getUserId, userId)
            .lt(Objects.nonNull(lastId), SalaryDiamondRunningWater::getId, lastId)
            .orderByDesc(SalaryDiamondRunningWater::getId)
            .last(PageConstant.formatLimit(size))
            .list();
  }

  @Override
  public List<SalaryDiamondRunningWater> appPage(Long userId, Integer size, Long lastId, String waterEvent) {
    return query()
            .eq(Objects.nonNull(waterEvent), SalaryDiamondRunningWater::getSalaryEvent, waterEvent)
            .eq(Objects.nonNull(userId), SalaryDiamondRunningWater::getUserId, userId)
            .lt(Objects.nonNull(lastId), SalaryDiamondRunningWater::getId, lastId)
            .orderByDesc(SalaryDiamondRunningWater::getId)
            .last(PageConstant.formatLimit(size))
            .list();
  }

  @Override
  public boolean existsTrackId(String trackId) {
    return Objects.nonNull(query()
            .select(SalaryDiamondRunningWater::getTrackId)
            .eq(SalaryDiamondRunningWater::getTrackId, trackId)
            .last(PageConstant.LIMIT_ONE)
            .getOne());
  }

  @Override
  public boolean existsTrackId(Long userId, String event, String trackId) {
    return Objects.nonNull(query()
            .select(SalaryDiamondRunningWater::getTrackId)
            .eq(SalaryDiamondRunningWater::getTrackId, trackId)
            .eq(SalaryDiamondRunningWater::getUserId, userId)
            .eq(SalaryDiamondRunningWater::getSalaryEvent, event)
            .last(PageConstant.LIMIT_ONE)
            .getOne());
  }

  @Override
  public Map<Integer, Long> getDiamondData(Long userId, Boolean isMonth) {
    List<SalaryDiamondRunningWater> list = query()
            .select(SalaryDiamondRunningWater::getCreateTime, SalaryDiamondRunningWater::getAmount)
            .eq(SalaryDiamondRunningWater::getUserId, userId)
            .eq(SalaryDiamondRunningWater::getType, 0)
            .in(SalaryDiamondRunningWater::getSalaryEvent,
                    List.of("SYSTEM_COMPENSATE","SYSTEM_SETTLEMENT_WAGES_AGENT","SYSTEM_SETTLEMENT_WAGES_MEMBER","CREATE_ACCOUNT"))
            .list();

    if (CollectionUtils.isEmpty(list)) {
      return Map.of();
    }

    if (isMonth) {
        return getMonthDataMap(list);
    }

    return getDayDataMap(list);
  }

  @Override
  public Map<Long, Long> getDiamondByUserIds(List<Long> userIds) {
    if (CollectionUtils.isEmpty(userIds)) {
      return new HashMap<>();
    }
    // 获取当前月份的第一天和最后一天
    YearMonth currentMonth = YearMonth.now();
    LocalDateTime firstDay = currentMonth.atDay(1).atStartOfDay();
    LocalDateTime lastDay = currentMonth.atEndOfMonth().atTime(23, 59, 59);

    // 将LocalDateTime转换为Timestamp
    Timestamp startTimestamp = Timestamp.valueOf(firstDay);
    Timestamp endTimestamp = Timestamp.valueOf(lastDay);

    List<SalaryDiamondRunningWater> list = query()
            .select(SalaryDiamondRunningWater::getUserId, SalaryDiamondRunningWater::getAmount)
            .in(SalaryDiamondRunningWater::getUserId, userIds)
            .eq(SalaryDiamondRunningWater::getType, 0)
            .in(SalaryDiamondRunningWater::getSalaryEvent,
                    List.of("SYSTEM_COMPENSATE","SYSTEM_SETTLEMENT_WAGES_AGENT","SYSTEM_SETTLEMENT_WAGES_MEMBER","CREATE_ACCOUNT"))
            .ge(SalaryDiamondRunningWater::getCreateTime, startTimestamp)
            .le(SalaryDiamondRunningWater::getCreateTime, endTimestamp)
            .list();

    if (CollectionUtils.isEmpty(list)) {
      return new HashMap<>();
    }

    return list.stream()
            .collect(Collectors.groupingBy(
                    SalaryDiamondRunningWater::getUserId,
                    Collectors.summingLong(item -> item.getAmount().longValue())
            ));
  }

  @NotNull
  private static Map<Integer, Long> getDayDataMap(List<SalaryDiamondRunningWater> list) {
    Map<Integer, Long> resultMap = new HashMap<>();
    for (SalaryDiamondRunningWater item : list) {
      Timestamp createTime = item.getCreateTime();
      LocalDateTime localDateTime = createTime.toInstant()
              .atZone(ZoneId.systemDefault())
              .toLocalDateTime();

      int yearMonthDay = localDateTime.getYear() * 10000 +
              localDateTime.getMonthValue() * 100 +
              localDateTime.getDayOfMonth();

      resultMap.put(yearMonthDay, resultMap.getOrDefault(yearMonthDay, 0L) + item.getAmount().longValue());
    }
    return resultMap;
  }

  @NotNull
  private static Map<Integer, Long> getMonthDataMap(List<SalaryDiamondRunningWater> list) {
    Map<Integer, Long> resultMap = new HashMap<>();
    for (SalaryDiamondRunningWater item : list) {
      Timestamp createTime = item.getCreateTime();
      LocalDateTime localDateTime = createTime.toInstant()
              .atZone(ZoneId.systemDefault())
              .toLocalDateTime();
      int yearMonth = localDateTime.getYear() * 100 + localDateTime.getMonthValue();
      resultMap.put(yearMonth, resultMap.getOrDefault(yearMonth, 0L) + item.getAmount().longValue());
    }
    return resultMap;
  }

}
