package com.emotion.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emotion.common.PageResult;
import com.emotion.dto.request.achievement.AchievementCreateRequest;
import com.emotion.dto.request.achievement.AchievementPageRequest;
import com.emotion.dto.request.achievement.AchievementUpdateRequest;
import com.emotion.dto.response.achievement.AchievementResponse;
import com.emotion.entity.Achievement;
import com.emotion.mapper.AchievementMapper;
import com.emotion.service.AchievementService;
import com.emotion.util.SnowflakeIdGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 成就服务实现类
 * 
 * @author huazhongmin
 * @date 2025-09-08
 */
@Service
public class AchievementServiceImpl extends ServiceImpl<AchievementMapper, Achievement> implements AchievementService {

    @Autowired
    private SnowflakeIdGenerator snowflakeIdGenerator;

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public IPage<Achievement> getPage(AchievementPageRequest request) {
        Page<Achievement> page = new Page<>(request.getCurrent(), request.getSize());
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        
        // 关键词搜索
        if (StringUtils.hasText(request.getKeyword())) {
            wrapper.and(w -> w.like(Achievement::getTitle, request.getKeyword())
                    .or().like(Achievement::getDescription, request.getKeyword()));
        }
        
        // 分类筛选
        if (StringUtils.hasText(request.getCategory())) {
            wrapper.eq(Achievement::getCategory, request.getCategory());
        }

        // 稀有度筛选
        if (StringUtils.hasText(request.getRarity())) {
            wrapper.eq(Achievement::getRarity, request.getRarity());
        }

        // 解锁状态筛选
        if (request.getUnlocked() != null) {
            if (request.getUnlocked()) {
                wrapper.isNotNull(Achievement::getUnlockedTime);
            } else {
                wrapper.isNull(Achievement::getUnlockedTime);
            }
        }

        // 隐藏状态筛选
        if (request.getHidden() != null) {
            wrapper.eq(Achievement::getIsHidden, request.getHidden() ? 1 : 0);
        }

        wrapper.eq(Achievement::getIsDeleted, 0);
        
        // 排序
        if (StringUtils.hasText(request.getOrderBy())) {
            if ("asc".equalsIgnoreCase(request.getOrderDirection())) {
                wrapper.orderByAsc(Achievement::getCreateTime);
            } else {
                wrapper.orderByDesc(Achievement::getCreateTime);
            }
        } else {
            wrapper.orderByDesc(Achievement::getCreateTime);
        }
        
        return this.page(page, wrapper);
    }

    @Override
    public List<Achievement> getByCategory(String category) {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Achievement::getCategory, category)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getCreateTime);
        return this.list(wrapper);
    }

    @Override
    public List<Achievement> getByRarity(String rarity) {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Achievement::getRarity, rarity)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getCreateTime);
        return this.list(wrapper);
    }

    @Override
    public List<Achievement> getByConditionType(String conditionType) {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Achievement::getConditionType, conditionType)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getCreateTime);
        return this.list(wrapper);
    }

    @Override
    public List<Achievement> getUnlockedAchievements() {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.isNotNull(Achievement::getUnlockedTime)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getUnlockedTime);
        return this.list(wrapper);
    }

    @Override
    public List<Achievement> getLockedAchievements() {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.isNull(Achievement::getUnlockedTime)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getCreateTime);
        return this.list(wrapper);
    }

    @Override
    public List<Achievement> getHiddenAchievements() {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Achievement::getIsHidden, 1)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getCreateTime);
        return this.list(wrapper);
    }

    @Override
    public List<Achievement> getVisibleAchievements() {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Achievement::getIsHidden, 0)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getCreateTime);
        return this.list(wrapper);
    }

    @Override
    public List<Achievement> getByProgressRange(Double minProgress, Double maxProgress) {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.between(Achievement::getProgress, minProgress, maxProgress)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getProgress);
        return this.list(wrapper);
    }

    @Override
    public List<Achievement> getByUnlockTimeRange(LocalDateTime startTime, LocalDateTime endTime) {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.between(Achievement::getUnlockedTime, startTime, endTime)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getUnlockedTime);
        return this.list(wrapper);
    }

    @Override
    public Long countUnlockedAchievements() {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.isNotNull(Achievement::getUnlockedTime)
               .eq(Achievement::getIsDeleted, 0);
        return this.count(wrapper);
    }

    @Override
    public Long countLockedAchievements() {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.isNull(Achievement::getUnlockedTime)
               .eq(Achievement::getIsDeleted, 0);
        return this.count(wrapper);
    }

    @Override
    public Long countByCategory(String category) {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Achievement::getCategory, category)
               .eq(Achievement::getIsDeleted, 0);
        return this.count(wrapper);
    }

    @Override
    public Long countByRarity(String rarity) {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Achievement::getRarity, rarity)
               .eq(Achievement::getIsDeleted, 0);
        return this.count(wrapper);
    }

    @Override
    public Double getAvgProgress() {
        List<Achievement> achievements = this.list(new LambdaQueryWrapper<Achievement>()
                .eq(Achievement::getIsDeleted, 0)
                .isNotNull(Achievement::getProgress));
        return achievements.stream()
                .mapToDouble(a -> a.getProgress() != null ? a.getProgress().doubleValue() : 0.0)
                .average()
                .orElse(0.0);
    }

    @Override
    public Double getAvgProgressByCategory(String category) {
        List<Achievement> achievements = this.list(new LambdaQueryWrapper<Achievement>()
                .eq(Achievement::getCategory, category)
                .eq(Achievement::getIsDeleted, 0)
                .isNotNull(Achievement::getProgress));
        return achievements.stream()
                .mapToDouble(a -> a.getProgress() != null ? a.getProgress().doubleValue() : 0.0)
                .average()
                .orElse(0.0);
    }

    @Override
    public List<Achievement> getRecentlyUnlocked(Integer limit) {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.isNotNull(Achievement::getUnlockedTime)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getUnlockedTime)
               .last("LIMIT " + limit);
        return this.list(wrapper);
    }

    @Override
    public List<Achievement> getNearCompletion() {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(Achievement::getProgress, 80)
               .isNull(Achievement::getUnlockedTime)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getProgress);
        return this.list(wrapper);
    }

    @Override
    public List<Achievement> getRareAchievements() {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(Achievement::getRarity, "legendary", "epic")
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getCreateTime);
        return this.list(wrapper);
    }

    @Override
    public boolean unlockAchievement(String id, LocalDateTime unlockedTime) {
        LambdaUpdateWrapper<Achievement> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(Achievement::getId, id)
               .set(Achievement::getUnlockedTime, unlockedTime)
               .set(Achievement::getProgress, 100)
               .set(Achievement::getUpdateTime, LocalDateTime.now());
        return this.update(wrapper);
    }

    @Override
    public boolean updateProgress(String id, Double progress) {
        LambdaUpdateWrapper<Achievement> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(Achievement::getId, id)
               .set(Achievement::getProgress, progress)
               .set(Achievement::getUpdateTime, LocalDateTime.now());
        return this.update(wrapper);
    }

    @Override
    public boolean updateHiddenStatus(String id, Integer isHidden) {
        LambdaUpdateWrapper<Achievement> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(Achievement::getId, id)
               .set(Achievement::getIsHidden, isHidden)
               .set(Achievement::getUpdateTime, LocalDateTime.now());
        return this.update(wrapper);
    }

    @Override
    public List<Achievement> getRecommendedAchievements(String category, String rarity, Integer limit) {
        LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Achievement::getCategory, category)
               .eq(Achievement::getRarity, rarity)
               .isNull(Achievement::getUnlockedTime)
               .eq(Achievement::getIsHidden, 0)
               .eq(Achievement::getIsDeleted, 0)
               .orderByDesc(Achievement::getCreateTime)
               .last("LIMIT " + limit);
        return this.list(wrapper);
    }

    // 新增的Response相关方法实现

    @Override
    public PageResult<AchievementResponse> getPageWithResponse(AchievementPageRequest request) {
        IPage<Achievement> page = getPage(request);
        List<AchievementResponse> responses = page.getRecords().stream()
                .map(this::convertToResponse)
                .collect(Collectors.toList());
        PageResult<AchievementResponse> pageResult = new PageResult<>();
        pageResult.setCurrent(page.getCurrent());
        pageResult.setSize(page.getSize());
        pageResult.setTotal(page.getTotal());
        pageResult.setPages(page.getPages());
        pageResult.setRecords(responses);
        return pageResult;
    }

    @Override
    public AchievementResponse getAchievementResponseById(String id) {
        Achievement achievement = this.getById(id);
        if (achievement == null) {
            return null;
        }
        return convertToResponse(achievement);
    }

    @Override
    public AchievementResponse createAchievementWithResponse(AchievementCreateRequest request) {
        Achievement achievement = new Achievement();
        org.springframework.beans.BeanUtils.copyProperties(request, achievement);
        achievement.setId(snowflakeIdGenerator.nextIdAsString());

        boolean saved = this.save(achievement);
        if (!saved) {
            return null;
        }
        return convertToResponse(achievement);
    }

    @Override
    public AchievementResponse updateAchievementWithResponse(AchievementUpdateRequest request) {
        Achievement achievement = this.getById(request.getId());
        if (achievement == null) {
            return null;
        }

        // 更新非空字段
        if (request.getTitle() != null) {
            achievement.setTitle(request.getTitle());
        }
        if (request.getDescription() != null) {
            achievement.setDescription(request.getDescription());
        }
        if (request.getCategory() != null) {
            achievement.setCategory(request.getCategory());
        }
        if (request.getIcon() != null) {
            achievement.setIcon(request.getIcon());
        }
        if (request.getRarity() != null) {
            achievement.setRarity(request.getRarity());
        }
        if (request.getConditionType() != null) {
            achievement.setConditionType(request.getConditionType());
        }
        if (request.getConditionValue() != null) {
            achievement.setConditionValue(request.getConditionValue());
        }
        if (request.getRewards() != null) {
            achievement.setRewards(request.getRewards());
        }
        if (request.getIsHidden() != null) {
            achievement.setIsHidden(request.getIsHidden());
        }

        achievement.setUpdateTime(LocalDateTime.now());

        boolean updated = this.updateById(achievement);
        if (!updated) {
            return null;
        }
        return convertToResponse(achievement);
    }

    @Override
    public List<AchievementResponse> getByCategoryWithResponse(String category) {
        List<Achievement> achievements = getByCategory(category);
        return achievements.stream()
                .map(this::convertToResponse)
                .collect(Collectors.toList());
    }

    @Override
    public List<AchievementResponse> getByRarityWithResponse(String rarity) {
        List<Achievement> achievements = getByRarity(rarity);
        return achievements.stream()
                .map(this::convertToResponse)
                .collect(Collectors.toList());
    }

    @Override
    public List<AchievementResponse> getUnlockedAchievementsWithResponse() {
        List<Achievement> achievements = getUnlockedAchievements();
        return achievements.stream()
                .map(this::convertToResponse)
                .collect(Collectors.toList());
    }

    @Override
    public List<AchievementResponse> getLockedAchievementsWithResponse() {
        List<Achievement> achievements = getLockedAchievements();
        return achievements.stream()
                .map(this::convertToResponse)
                .collect(Collectors.toList());
    }

    @Override
    public List<AchievementResponse> getRecentlyUnlockedWithResponse(Integer limit) {
        List<Achievement> achievements = getRecentlyUnlocked(limit);
        return achievements.stream()
                .map(this::convertToResponse)
                .collect(Collectors.toList());
    }

    /**
     * 转换为响应对象
     */
    private AchievementResponse convertToResponse(Achievement achievement) {
        AchievementResponse response = new AchievementResponse();
        org.springframework.beans.BeanUtils.copyProperties(achievement, response);
        response.setId(achievement.getId());
        if (achievement.getCreateTime() != null) {
            response.setCreateTime(achievement.getCreateTime().format(DATE_TIME_FORMATTER));
        }
        if (achievement.getUpdateTime() != null) {
            response.setUpdateTime(achievement.getUpdateTime().format(DATE_TIME_FORMATTER));
        }
        if (achievement.getUnlockedTime() != null) {
            response.setUnlockedTime(achievement.getUnlockedTime().format(DATE_TIME_FORMATTER));
        }
        return response;
    }
}