package com.yolo.keyboard.service.character;

import java.util.*;
import java.util.concurrent.TimeUnit;

import com.yolo.keyboard.controller.admin.character.vo.*;
import com.yolo.keyboard.dal.dataobject.character.CharacterI18nDO;
import com.yolo.keyboard.dal.dataobject.character.KeyboardCharacterDO;
import com.yolo.keyboard.dal.mysql.character.CharacterI18nMapper;
import com.yolo.keyboard.framework.common.pojo.PageParam;
import com.yolo.keyboard.framework.common.pojo.PageResult;
import com.yolo.keyboard.framework.common.util.object.BeanUtils;
import com.yolo.keyboard.dal.mysql.character.KeyboardCharacterMapper;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import static com.yolo.keyboard.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.CHARACTER_I18N_NOT_EXISTS;
import static com.yolo.keyboard.module.infra.enums.ErrorCodeConstants.CHARACTER_NOT_EXISTS;


/**
 * 键盘人设 Service 实现类
 *
 * @author ziin
 */
@Service
@Validated
public class KeyboardCharacterServiceImpl implements KeyboardCharacterService {

    private static final String CHARACTER_CACHE_KEY_PREFIX = "character:";
    private static final long CHARACTER_CACHE_TTL_MINUTES = 5L;

    @Resource
    private CharacterI18nMapper characterI18nMapper;

    @Resource
    private KeyboardCharacterMapper characterMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public Long createCharacter(KeyboardCharacterSaveReqVO createReqVO) {
        // 插入
        KeyboardCharacterDO character = BeanUtils.toBean(createReqVO, KeyboardCharacterDO.class);
        characterMapper.insert(character);

        // 同步刷新 Redis 缓存，保证缓存内容与数据库最终落库数据一致
        refreshCharacterCache(character.getId());

        // 返回
        return character.getId();
    }

    @Override
    public void updateCharacter(KeyboardCharacterSaveReqVO updateReqVO) {
        // 校验存在
        validateCharacterExists(updateReqVO.getId());
        // 更新
        KeyboardCharacterDO updateObj = BeanUtils.toBean(updateReqVO, KeyboardCharacterDO.class);
        characterMapper.updateById(updateObj);

        // 删除 Redis 缓存，下次读取时再从数据库加载最新数据
        deleteCharacterCache(updateReqVO.getId());
    }

    @Override
    public void deleteCharacter(Long id) {
        // 校验存在
        validateCharacterExists(id);
        // 删除
        characterMapper.deleteById(id);

        // 同步删除 Redis 缓存，避免读取到已删除数据
        deleteCharacterCache(id);
    }

    @Override
    public void deleteCharacterListByIds(List<Long> ids) {
        // 删除
        characterMapper.deleteByIds(ids);

        // 同步删除 Redis 缓存，避免批量删除后残留脏数据
        deleteCharacterCaches(ids);
    }

    // ==================== 子表（键盘人设国际化内容） ====================

    @Override
    public PageResult<CharacterI18nDO> getCharacterI18nPage(PageParam pageReqVO, Long characterId) {
        return characterI18nMapper.selectPage(pageReqVO, characterId);
    }

    @Override
    public Long createCharacterI18n(CharacterI18nDO characterI18n) {
        characterI18nMapper.insert(characterI18n);
        return characterI18n.getId();
    }

    @Override
    public void updateCharacterI18n(CharacterI18nDO characterI18n) {
        // 校验存在
        validateCharacterI18nExists(characterI18n.getId());
        // 更新
        characterI18nMapper.updateById(characterI18n);
    }

    @Override
    public void deleteCharacterI18n(Long id) {
        // 删除
        characterI18nMapper.deleteById(id);
    }

    @Override
    public void deleteCharacterI18nListByIds(List<Long> ids) {
        // 删除
        characterI18nMapper.deleteByIds(ids);
    }

    @Override
    public CharacterI18nDO getCharacterI18n(Long id) {
        return characterI18nMapper.selectById(id);
    }

    private void validateCharacterI18nExists(Long id) {
        if (characterI18nMapper.selectById(id) == null) {
            throw exception(CHARACTER_I18N_NOT_EXISTS);
        }
    }

    private void deleteCharacterI18nByCharacterId(Long characterId) {
        characterI18nMapper.deleteByCharacterId(characterId);
    }

    private void deleteCharacterI18nByCharacterIds(List<Long> characterIds) {
        characterI18nMapper.deleteByCharacterIds(characterIds);
    }



    private void validateCharacterExists(Long id) {
        if (characterMapper.selectById(id) == null) {
            throw exception(CHARACTER_NOT_EXISTS);
        }
    }

    @Override
    public KeyboardCharacterDO getCharacter(Long id) {
        return characterMapper.selectById(id);
    }

    @Override
    public PageResult<KeyboardCharacterDO> getCharacterPage(KeyboardCharacterPageReqVO pageReqVO) {
        return characterMapper.selectPage(pageReqVO);
    }

    private void refreshCharacterCache(Long characterId) {
        if (characterId == null) {
            return;
        }
        String cacheKey = CHARACTER_CACHE_KEY_PREFIX + characterId;
        KeyboardCharacterDO latestCharacter = characterMapper.selectById(characterId);
        if (latestCharacter == null) {
            redisTemplate.delete(cacheKey);
            return;
        }
        redisTemplate.opsForValue().set(cacheKey, latestCharacter, CHARACTER_CACHE_TTL_MINUTES, TimeUnit.MINUTES);
    }

    private void deleteCharacterCache(Long characterId) {
        if (characterId == null) {
            return;
        }
        redisTemplate.delete(buildCharacterCacheKey(characterId));
    }

    private void deleteCharacterCaches(List<Long> characterIds) {
        if (characterIds == null || characterIds.isEmpty()) {
            return;
        }
        Set<String> cacheKeys = new HashSet<>();
        for (Long characterId : characterIds) {
            if (characterId != null) {
                cacheKeys.add(buildCharacterCacheKey(characterId));
            }
        }
        if (!cacheKeys.isEmpty()) {
            redisTemplate.delete(cacheKeys);
        }
    }

    private String buildCharacterCacheKey(Long characterId) {
        return CHARACTER_CACHE_KEY_PREFIX + characterId;
    }

}
