package com.red.circle.other.app.inner.service.material.props.impl;

import com.red.circle.other.app.inner.convertor.material.PropsInnerConvertor;
import com.red.circle.other.app.inner.service.material.props.PropsBackpackClientService;
import com.red.circle.other.domain.gateway.props.PropsStoreGateway;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.common.props.PropsBackpackCommon;
import com.red.circle.other.infra.database.rds.entity.props.PropsBackpack;
import com.red.circle.other.infra.database.rds.entity.props.RunningWaterUserProps;
import com.red.circle.other.infra.database.rds.service.props.PropsBackpackService;
import com.red.circle.other.infra.database.rds.service.props.RunningWaterUserPropsService;
import com.red.circle.other.inner.enums.material.PropsCommodityType;
import com.red.circle.other.inner.model.cmd.material.DeletePropsCmd;
import com.red.circle.other.inner.model.cmd.material.GivePropsBackpackCmd;
import com.red.circle.other.inner.model.cmd.material.PropsBackpackQry;
import com.red.circle.other.inner.model.cmd.material.ReduceDaysAndUnUseCmd;
import com.red.circle.other.inner.model.dto.material.PropsBackpackDTO;
import com.red.circle.other.inner.model.dto.material.UsePropsDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

/**
 * 用户道具客户端 服务实现.
 *
 * @author pengliang on 2023/6/3
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PropsBackpackClientServiceImpl implements PropsBackpackClientService {

  private final PropsStoreGateway propsStoreGateway;
  private final UserProfileGateway userProfileGateway;
  private final PropsBackpackCommon propsBackpackCommon;
  private final PropsInnerConvertor propsInnerConvertor;
  private final PropsBackpackService propsBackpackService;
  private final RunningWaterUserPropsService runningWaterUserPropsService;

  @Override
  public UsePropsDTO switchUseProps(Long userId, Long propsId) {
    return propsBackpackCommon.switchUseProps(userId, propsId);
  }

  @Override
  public void unloadUseProps(Long userId, PropsCommodityType propsType) {
    propsBackpackCommon.unloadUseProps(userId, propsType);
  }

  @Override
  public void deleteProps(DeletePropsCmd cmd) {
    propsBackpackService.deleteProps(cmd.getUserIds(),
        cmd.getPropsIds(),
        cmd.getPropsType()
    );
  }

  @Override
  public void saveBatchBackpack(List<PropsBackpackDTO> props) {
    propsBackpackService.saveBatch(propsInnerConvertor.toListPropsBackpack(props));
  }

  @Override
  public void saveBackpack(PropsBackpackDTO props) {
    propsBackpackService.save(propsInnerConvertor.toPropsBackpack(props));
  }

  @Override
  public void giveProps(GivePropsBackpackCmd cmd) {

    List<PropsBackpack> list = propsBackpackService.query()
            .eq(PropsBackpack::getUserId, cmd.getAcceptUserId())
            .eq(PropsBackpack::getType, cmd.getType())
            .gt(PropsBackpack::getExpireTime, TimestampUtils.now())
            .list();
    if (CollectionUtils.isNotEmpty(list)) {
      // 有使用的道具 && 使用的道具和当前道具不同  => 有同类型的旧道具在使用中
      boolean anyMatch = list.stream()
          .anyMatch(e -> e.getUseProps() && !e.getPropsId().equals(cmd.getPropsId()));
      if (anyMatch) {
        cmd.setUseProps(Boolean.FALSE);
      }
    }

    PropsBackpack oldpropsBackpack = propsBackpackService.getUserPropsByUserIdByPropsId(cmd.getAcceptUserId(), cmd.getPropsId());

    if (cmd.getDays() == 0) {
      throw new IllegalArgumentException("天数不能为0");
    }
    if (oldpropsBackpack == null && cmd.getDays() <= 0) {
      throw new IllegalArgumentException("该用户没有此道具,天数必须大于0");
    }

    Timestamp timestamp = getTimestamp(cmd, oldpropsBackpack);

    propsBackpackService.addPropsBackpack(
        new PropsBackpack()
            .setUserId(cmd.getAcceptUserId())
            .setPropsId(cmd.getPropsId())
            .setType(cmd.getType())
            .setExpireTime(timestamp)
            .setUseProps(cmd.getUseProps())
            .setAllowGive(cmd.getAllowGive())
    );

    runningWaterUserPropsService.save(new RunningWaterUserProps()
        .setUserId(cmd.getAcceptUserId())
        .setPropsId(cmd.getPropsId())
        .setPropsCandy(BigDecimal.ZERO)
        .setOrigin(cmd.getOrigin())
        .setOriginDesc(cmd.getOriginDesc())
        .setDays(cmd.getDays())
        .setType(cmd.getType())
        .withCreateUser(cmd.getOpUser()));

    userProfileGateway.removeCacheAll(cmd.getAcceptUserId());
  }

  @NotNull
  private static Timestamp getTimestamp(GivePropsBackpackCmd cmd, PropsBackpack oldpropsBackpack) {
    Timestamp timestamp = null;
    if (oldpropsBackpack == null || oldpropsBackpack.getExpireTime().before(TimestampUtils.now())) {
      timestamp = TimestampUtils.nowPlusDays(cmd.getDays());
    } else {
      timestamp = TimestampUtils.dateTimePlusDays(oldpropsBackpack.getExpireTime(), cmd.getDays());
    }
    return timestamp;
  }

  @Override
  public List<PropsBackpackDTO> listNotExpiredUseProps(Long userId) {
    return propsInnerConvertor.toListPropsBackpackDTO(
        propsBackpackService.listNotExpiredUseProps(userId));
  }

  @Override
  public List<PropsBackpackDTO> listNotExpiredUseProps(Set<Long> userIds) {
    return propsInnerConvertor.toListPropsBackpackDTO(
        propsBackpackService.listNotExpiredUseProps(userIds)
    );
  }

  @Override
  public Map<Long, List<UsePropsDTO>> mapNotExpiredUseProps(PropsBackpackQry qry) {
    return propsInnerConvertor.toMapUsePropsDTO(
        propsStoreGateway.mapUserUsePropsByIdTypes(qry.getUserIds(), qry.getPropsTypes()));
  }

  @Override
  public List<UsePropsDTO> listNotExpiredUseProps(Long userId,
      List<PropsCommodityType> propsTypes) {
    return propsInnerConvertor.toListUsePropsDTO(
        propsStoreGateway.listUserUsePropsByIdTypes(Set.of(userId), propsTypes));
  }

  @Override
  public void reduceDaysAndUnUse(ReduceDaysAndUnUseCmd cmd) {
    propsBackpackService.reduceDaysAndUnUse(cmd.getUserId(), cmd.getPropsIds(),
        cmd.getReduceDays());
  }

  @Override
  public List<PropsBackpackDTO> listNotExpiredByType(Long userId, PropsCommodityType type) {
    return propsInnerConvertor.toListPropsBackpackDTO(
        propsBackpackService.listNotExpiredByType(userId, type));
  }

}
