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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.app.inner.convertor.material.PropsResourcesInnerConvertor;
import com.red.circle.other.app.inner.service.material.props.PropsSourceClientService;
import com.red.circle.other.infra.common.props.PropsResourcesCommon;
import com.red.circle.other.infra.database.cache.key.AdminFreeKeys;
import com.red.circle.other.infra.database.rds.entity.badge.BadgeConfig;
import com.red.circle.other.infra.database.rds.entity.badge.BadgePictureConfig;
import com.red.circle.other.infra.database.rds.entity.family.FamilyLevelConfig;
import com.red.circle.other.infra.database.rds.entity.props.PropsRedPacketSkin;
import com.red.circle.other.infra.database.rds.entity.props.PropsSourceRecord;
import com.red.circle.other.infra.database.rds.service.badge.BadgeConfigService;
import com.red.circle.other.infra.database.rds.service.badge.BadgePictureConfigService;
import com.red.circle.other.infra.database.rds.service.family.FamilyLevelConfigService;
import com.red.circle.other.infra.database.rds.service.props.PropsRedPacketSkinService;
import com.red.circle.other.infra.database.rds.service.props.PropsSourceRecordService;
import com.red.circle.other.infra.utils.RedisUtils;
import com.red.circle.other.inner.enums.material.ConsolePropsTypeEnum;
import com.red.circle.other.inner.enums.material.PropsTypeEnum;
import com.red.circle.other.inner.enums.sys.SysBadgeConfigTypeEnum;
import com.red.circle.other.inner.model.cmd.material.PropsResourcesMergeCmd;
import com.red.circle.other.inner.model.cmd.material.PropsSourceRecordQryCmd;
import com.red.circle.other.inner.model.dto.material.PropsRedPacketSkinDTO;
import com.red.circle.other.inner.model.dto.material.PropsResourcesDTO;
import com.red.circle.other.inner.model.dto.material.PropsResourcesMergeDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 道具资源.
 *
 * @author pengliang on 2023/11/7
 */
@Service
@RequiredArgsConstructor
public class PropsSourceClientServiceImpl implements PropsSourceClientService {

  private static final String BADGE_TYPE = PropsTypeEnum.BADGE.name();
  private static final String DEFAULT_BADGE_SOURCE_TYPE = SysBadgeConfigTypeEnum.ACTIVITY.getName();
  private static final String BADGE_DISPLAY_TYPE_LONG = "LONG";
  private static final String BADGE_DISPLAY_TYPE_SHORT = "SHORT";

  private final PropsResourcesCommon propsResourcesCommon;
  private final FamilyLevelConfigService familyLevelConfigService;
  private final PropsSourceRecordService propsSourceRecordService;
  private final PropsRedPacketSkinService propsRedPacketSkinService;
  private final BadgeConfigService badgeConfigService;
  private final BadgePictureConfigService badgePictureConfigService;
  private final RedisService redisService;
  @Autowired
  @Qualifier("propsResourcesInnerConvertorImplNew")
  private PropsResourcesInnerConvertor propsResourcesInnerConvertor;

  @Override
  public PropsResourcesDTO getById(Long id) {
    return propsResourcesInnerConvertor.toPropsResourcesDTO(propsSourceRecordService.getById(id));
  }

  @Override
  public Map<Long, PropsResourcesDTO> mapByIds(Set<Long> ids) {
    return propsResourcesInnerConvertor.toMapPropsResourcesDTO(
        propsSourceRecordService.mapByIds(ids));
  }

  @Override
  public PropsResourcesMergeDTO mapMergeByIds(PropsResourcesMergeCmd cmd) {
    return propsResourcesCommon.mapMergeByIds(cmd);
  }

  @Override
  public List<PropsResourcesDTO> listSysOriginPropsResources(String sysOrigin, String type) {
    return propsResourcesInnerConvertor.toListPropsResourcesDTO(
        propsSourceRecordService.listSysOrigin(sysOrigin, type));
  }

  @Override
  public List<PropsResourcesDTO> listExcludeFamily(String sysOrigin, String type) {
    return propsResourcesInnerConvertor.toListPropsResourcesDTO(
        propsSourceRecordService.listSysOrigin(sysOrigin, type,
            levelAvatarFrameIds(type, familyLevelConfigService.listAllLevel()))
    );
  }

  private Set<Long> levelAvatarFrameIds(String type, List<FamilyLevelConfig> levelConfigs) {
    Set<Long> props = null;

    if (CollectionUtils.isNotEmpty(levelConfigs) && Objects
        .equals(type, ConsolePropsTypeEnum.AVATAR_FRAME.getName())) {

      props = levelConfigs.stream().map(FamilyLevelConfig::getAvatarFrameId)
          .collect(Collectors.toSet());
    }
    return props;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void offShelfPropsResources(Long id, Boolean offShelf) {
    PropsSourceRecord sourceRecord = propsSourceRecordService.getById(id);
    propsSourceRecordService.offShelf(id, offShelf);
    if (Objects.nonNull(sourceRecord) && Objects.equals(BADGE_TYPE, sourceRecord.getType())) {
      badgeConfigService.update()
          .set(BadgeConfig::getDel, Boolean.TRUE.equals(offShelf))
          .eq(BadgeConfig::getId, id)
          .execute();
    }
  }

  @Override
  public PageResult<PropsResourcesDTO> pagePropsResourcesOps(PropsSourceRecordQryCmd qryCmd) {
    return propsResourcesInnerConvertor.toPagePropsResourcesDTO(
        propsSourceRecordService.query()
            .eq(PropsSourceRecord::getSysOrigin, qryCmd.getSysOrigin())
            .eq(Objects.nonNull(qryCmd.getType()), PropsSourceRecord::getType,
                qryCmd.getType())
            .eq(Objects.nonNull(qryCmd.getId()), PropsSourceRecord::getId,
                qryCmd.getId())
            .like(StringUtils.isNotBlank(qryCmd.getCode()), PropsSourceRecord::getCode,
                qryCmd.getCode())
            .eq(Objects.nonNull(qryCmd.getDel()), PropsSourceRecord::getDel,
                qryCmd.getDel())
            .eq(Objects.nonNull(qryCmd.getAdminFree()), PropsSourceRecord::getAdminFree,
                qryCmd.getAdminFree())
            .ge(Objects.nonNull(qryCmd.getAmountMin()), PropsSourceRecord::getAmount,
                qryCmd.getAmountMin())
            .le(Objects.nonNull(qryCmd.getAmountMax()), PropsSourceRecord::getAmount,
                qryCmd.getAmountMax())
            .like(StringUtils.isNotBlank(qryCmd.getName()), PropsSourceRecord::getName,
                qryCmd.getName())
            .orderByDesc(PropsSourceRecord::getCreateTime)
            .page(qryCmd.getPageQuery())
    );
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void addOrUpdatePropsResources(PropsResourcesDTO dto) {
    PropsSourceRecord propsSourceRecord = propsResourcesInnerConvertor.toPropsSourceRecord(dto);
    PropsSourceRecord beforeUpdate = Objects.nonNull(dto.getId())
        ? propsSourceRecordService.getById(dto.getId())
        : null;
    if (Objects.isNull(dto.getId())) {
      propsSourceRecordService.save(propsSourceRecord);
      syncBadgeConfig(dto, propsSourceRecord);
      if (Objects.equals(PropsTypeEnum.RED_PACKET.name(), propsSourceRecord.getType())) {
        propsRedPacketSkinService.save(new PropsRedPacketSkin().setId(propsSourceRecord.getId())
            .setImNotOpenedUrl(dto.getImNotOpenedUrl())
            .setImOpenedUrl(dto.getImOpenedUrl())
            .setImSendCoverUrl(dto.getImSendCoverUrl())
            .setRoomSendCoverUrl(dto.getRoomSendCoverUrl())
            .setRoomNotOpenedUrl(dto.getRoomNotOpenedUrl())
            .setRoomOpenedUrl(dto.getRoomOpenedUrl())
            .setImOpenedUrlTwo(dto.getImOpenedUrlTwo())
            .setSysOrigin(dto.getSysOrigin()));
      }
      return;
    }
    propsSourceRecordService.updateSelectiveById(propsSourceRecord);
    if (Objects.nonNull(beforeUpdate) && Objects.equals(BADGE_TYPE, beforeUpdate.getType())
        && !Objects.equals(BADGE_TYPE, propsSourceRecord.getType())) {
      badgeConfigService.update()
          .set(BadgeConfig::getDel, Boolean.TRUE)
          .eq(BadgeConfig::getId, propsSourceRecord.getId())
          .execute();
    }
    syncBadgeConfig(dto, propsSourceRecord);

    if (Objects.equals(PropsTypeEnum.RED_PACKET.name(), propsSourceRecord.getType())) {

      propsRedPacketSkinService.updateSelectiveById(
          new PropsRedPacketSkin().setId(propsSourceRecord.getId())
              .setImNotOpenedUrl(dto.getImNotOpenedUrl())
              .setImOpenedUrl(dto.getImOpenedUrl())
              .setImSendCoverUrl(dto.getImSendCoverUrl())
              .setRoomSendCoverUrl(dto.getRoomSendCoverUrl())
              .setRoomNotOpenedUrl(dto.getRoomNotOpenedUrl())
              .setRoomOpenedUrl(dto.getRoomOpenedUrl())
              .setImOpenedUrlTwo(dto.getImOpenedUrlTwo())
              .setSysOrigin(dto.getSysOrigin()));
    }
  }

  private void syncBadgeConfig(PropsResourcesDTO dto, PropsSourceRecord propsSourceRecord) {
    if (!Objects.equals(BADGE_TYPE, propsSourceRecord.getType())) {
      return;
    }
    Long badgeId = propsSourceRecord.getId();
    if (Objects.isNull(badgeId)) {
      return;
    }
    JSONObject expand = parseExpand(dto.getExpand());
    String badgeSourceType = normalizeBadgeSourceType(expand.getString("badgeType"));
    String badgeDisplayType = normalizeBadgeDisplayType(
        firstNotBlank(expand.getString("badgeDisplayType"), expand.getString("displayType")),
        badgeSourceType);
    Integer badgeLevel = expand.getInteger("badgeLevel");
    Long milestone = expand.getLong("milestone");
    String badgeKey = StringUtils.isNotBlank(expand.getString("badgeKey"))
        ? expand.getString("badgeKey")
        : propsSourceRecord.getCode();

    BadgeConfig badgeConfig = new BadgeConfig()
        .setId(badgeId)
        .setType(badgeSourceType)
        .setDisplayType(badgeDisplayType)
        .setBadgeLevel(Objects.nonNull(badgeLevel) ? badgeLevel : 0)
        .setMilestone(milestone)
        .setBadgeName(propsSourceRecord.getName())
        .setBadgeKey(badgeKey)
        .setDel(Boolean.TRUE.equals(propsSourceRecord.getDel()));
    if (Objects.isNull(badgeConfigService.getById(badgeId))) {
      badgeConfigService.save(badgeConfig);
    } else {
      badgeConfigService.updateSelectiveById(badgeConfig);
    }

    BadgePictureConfig pictureConfig = badgePictureConfigService
        .getByConfigIdBySysOrigin(badgeId, propsSourceRecord.getSysOrigin());
    String notSelectUrl = expand.getString("notSelectUrl");
    if (Objects.isNull(pictureConfig)) {
      badgePictureConfigService.save(new BadgePictureConfig()
          .setBadgeConfigId(badgeId)
          .setSysOrigin(propsSourceRecord.getSysOrigin())
          .setSelectUrl(propsSourceRecord.getCover())
          .setNotSelectUrl(notSelectUrl)
          .setAnimationUrl(propsSourceRecord.getSourceUrl()));
      return;
    }
    pictureConfig.setSelectUrl(propsSourceRecord.getCover());
    pictureConfig.setNotSelectUrl(notSelectUrl);
    pictureConfig.setAnimationUrl(propsSourceRecord.getSourceUrl());
    badgePictureConfigService.updateSelectiveById(pictureConfig);
  }

  private JSONObject parseExpand(String expand) {
    if (StringUtils.isBlank(expand)) {
      return new JSONObject();
    }
    try {
      return JSON.parseObject(expand);
    } catch (Exception ex) {
      return new JSONObject();
    }
  }

  private String normalizeBadgeSourceType(String badgeType) {
    if (StringUtils.isBlank(badgeType)) {
      return DEFAULT_BADGE_SOURCE_TYPE;
    }
    for (SysBadgeConfigTypeEnum value : SysBadgeConfigTypeEnum.values()) {
      if (Objects.equals(value.getName(), badgeType)) {
        return badgeType;
      }
    }
    return DEFAULT_BADGE_SOURCE_TYPE;
  }

  private String normalizeBadgeDisplayType(String displayType, String badgeSourceType) {
    if (StringUtils.isNotBlank(displayType)) {
      String normalized = displayType.trim().toUpperCase();
      if (Objects.equals(BADGE_DISPLAY_TYPE_LONG, normalized)
          || Objects.equals(BADGE_DISPLAY_TYPE_SHORT, normalized)) {
        return normalized;
      }
    }
    return Objects.equals(SysBadgeConfigTypeEnum.VIP.getName(), badgeSourceType)
        ? BADGE_DISPLAY_TYPE_LONG
        : BADGE_DISPLAY_TYPE_SHORT;
  }

  private String firstNotBlank(String... values) {
    for (String value : values) {
      if (StringUtils.isNotBlank(value)) {
        return value;
      }
    }
    return null;
  }

  @Override
  public PropsRedPacketSkinDTO getPropsRedPacketSkin(Long id) {

    return propsResourcesInnerConvertor.toPropsRedPacketSkinDTO(
        propsRedPacketSkinService.getById(id));
  }

  @Override
  public void updateRedisCache(PropsResourcesDTO dto) {
    Set<String> strings = RedisUtils.redisScan(redisService.redisTemplate(), AdminFreeKeys.ADMIN_FREE_PROPS.getKey());
    if (CollectionUtils.isNotEmpty(strings)) {

      strings.forEach(s -> {
        //先判断是否存在hash中
        if (!redisService.hashHasKey(s,dto.getId().toString())) {
          if (dto.getAdminFree()) {
            //不存在 新增
            dto.setCount(50);
            redisService.hashPut(s, String.valueOf(dto.getId()), JSON.toJSONString(dto));
          }
        }else{
            if (!dto.getAdminFree()) {
                redisService.hashDelete(s, dto.getId().toString());
            }
        }
      });
    }

  }
}
