package com.red.circle.other.infra.gateway;

import com.red.circle.other.domain.ranking.RankingActivityType;
import com.red.circle.other.domain.ranking.RankingCycleType;
import com.red.circle.other.infra.database.mongo.entity.activity.RankingActivityRecord;
import com.red.circle.other.infra.database.mongo.service.activity.RankingActivityRecordRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Component;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.sql.Timestamp;
import java.util.List;
import java.util.Optional;

/**
 * 排行榜活动领域网关实现
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RankingActivityGatewayImpl implements RankingActivityGateway {

    private final RankingActivityRecordRepository repository;
    private final MongoTemplate mongoTemplate;

    private static final Integer NOT_DELETED = 0;

    @Override
    public RankingActivityRecord accumulateRankingData(RankingActivityRecord record, Long incrementQuantity) {
        Timestamp now = new Timestamp(System.currentTimeMillis());

        // 构建查询条件：根据业务唯一键
        Query query = new Query(Criteria
                .where("sysOrigin").is(record.getSysOrigin())
                .and("userId").is(record.getUserId())
                .and("activityType").is(record.getActivityType())
                .and("cycleKey").is(record.getCycleKey())
                .and("deleted").is(NOT_DELETED)
        );

        // 构建更新操作
        Update update = new Update()
                .inc("quantity", incrementQuantity)
                .set("updateTime", now)

                .setOnInsert("sysOrigin", record.getSysOrigin())
                .setOnInsert("userId", record.getUserId())
                .setOnInsert("userSex", record.getUserSex())
                .setOnInsert("activityType", record.getActivityType())
                .setOnInsert("cycleType", record.getCycleType())
                .setOnInsert("cycleKey", record.getCycleKey())
                .setOnInsert("dimension", record.getDimension())
                .setOnInsert("extData", record.getExtData())
                .setOnInsert("expiredTime", record.getExpiredTime())
                .setOnInsert("createTime", now)
                .setOnInsert("deleted", NOT_DELETED);

        FindAndModifyOptions options = new FindAndModifyOptions()
                .upsert(true)       // 不存在则插入
                .returnNew(true);   // 返回更新后的文档
        return mongoTemplate.findAndModify(query, update, options, RankingActivityRecord.class);
    }

    @Override
    public Optional<RankingActivityRecord> getUserRecord(
            String sysOrigin,
            Long userId,
            RankingActivityType activityType,
            String cycleKey) {
        return repository.findBySysOriginAndUserIdAndActivityTypeAndCycleKeyAndDeleted(
                sysOrigin, userId, activityType, cycleKey, NOT_DELETED
        );
    }

    @Override
    public List<RankingActivityRecord> getRankingList(
            String sysOrigin,
            RankingActivityType activityType,
            RankingCycleType cycleType,
            String cycleKey,
            Integer userSex,
            Integer topN) {
        
        PageRequest pageRequest = PageRequest.of(0, topN);
        
        if (userSex == null) {
            // 不限性别
            return repository.findBySysOriginAndActivityTypeAndCycleTypeAndCycleKeyAndDeletedOrderByQuantityDescCreateTimeAsc(
                    sysOrigin, activityType, cycleType, cycleKey, NOT_DELETED, pageRequest
            );
        } else {
            // 按性别筛选
            return repository.findBySysOriginAndActivityTypeAndCycleTypeAndCycleKeyAndUserSexAndDeletedOrderByQuantityDescCreateTimeAsc(
                    sysOrigin, activityType, cycleType, cycleKey, userSex, NOT_DELETED, pageRequest
            );
        }
    }

    @Override
    public Integer calculateUserRank(
            String sysOrigin,
            RankingActivityType activityType,
            RankingCycleType cycleType,
            String cycleKey,
            Integer userSex,
            Long quantity) {
        
        Long countGreater;
        
        if (userSex == null) {
            // 不限性别
            countGreater = repository.countByQuantityGreaterThanWithoutSex(
                    sysOrigin, activityType, cycleType, cycleKey, NOT_DELETED, quantity
            );
        } else {
            // 按性别筛选
            countGreater = repository.countByQuantityGreaterThan(
                    sysOrigin, activityType, cycleType, cycleKey, userSex, NOT_DELETED, quantity
            );
        }
        
        // 排名 = 比我多的人数 + 1
        return Math.toIntExact((countGreater == null ? 0 : countGreater) + 1);
    }

    @Override
    public Long countTotalParticipants(
            String sysOrigin,
            RankingActivityType activityType,
            RankingCycleType cycleType,
            String cycleKey,
            Integer userSex) {
        
        if (userSex == null) {
            // 不限性别
            return repository.countBySysOriginAndActivityTypeAndCycleTypeAndCycleKeyAndDeleted(
                    sysOrigin, activityType, cycleType, cycleKey, NOT_DELETED
            );
        } else {
            // 按性别筛选
            return repository.countBySysOriginAndActivityTypeAndCycleTypeAndCycleKeyAndUserSexAndDeleted(
                    sysOrigin, activityType, cycleType, cycleKey, userSex, NOT_DELETED
            );
        }
    }

    @Override
    public RankingActivityRecord save(RankingActivityRecord record) {
        return repository.save(record);
    }

    @Override
    public List<RankingActivityRecord> batchGetUserRecords(
            List<Long> userIds,
            RankingActivityType activityType,
            String cycleKey) {
        return repository.findByUserIdInAndActivityTypeAndCycleKeyAndDeleted(
                userIds, activityType, cycleKey, NOT_DELETED
        );
    }
}