package com.kym.service.admin.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.github.pagehelper.PageHelper;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.kym.common.exception.BusinessException;
import com.kym.common.utils.CommUtil;
import com.kym.common.utils.IDGenerator;
import com.kym.entity.admin.Station;
import com.kym.entity.admin.StationWhitelist;
import com.kym.entity.admin.StationWhitelistLog;
import com.kym.entity.admin.dto.BatchAddWhitelistDto;
import com.kym.entity.admin.queryParams.StationWhitelistQueryParam;
import com.kym.entity.admin.vo.StationWhitelistVo;
import com.kym.entity.common.PageBean;
import com.kym.entity.common.RedisKeys;
import com.kym.entity.miniapp.User;
import com.kym.mapper.admin.StationWhitelistMapper;
import com.kym.service.admin.StationService;
import com.kym.service.admin.StationWhitelistLogService;
import com.kym.service.admin.StationWhitelistService;
import com.kym.service.miniapp.UserService;
import jakarta.annotation.PostConstruct;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
@DS("db-admin")
public class StationWhitelistServiceImpl extends MPJBaseServiceImpl<StationWhitelistMapper, StationWhitelist> implements StationWhitelistService {

    private static final long CACHE_EXPIRE_HOURS = 24;
    private final IDGenerator idGenerator = new IDGenerator();
    private final StationWhitelistMapper stationWhitelistMapper;
    private final StationService stationService;
    private final UserService userService;
    private final StationWhitelistLogService stationWhitelistLogService;
    private final StringRedisTemplate redisTemplate;

    public StationWhitelistServiceImpl(StationWhitelistMapper stationWhitelistMapper,
                                        StationService stationService,
                                        UserService userService,
                                        StationWhitelistLogService stationWhitelistLogService,
                                        StringRedisTemplate redisTemplate) {
        this.stationWhitelistMapper = stationWhitelistMapper;
        this.stationService = stationService;
        this.userService = userService;
        this.stationWhitelistLogService = stationWhitelistLogService;
        this.redisTemplate = redisTemplate;
    }

    @PostConstruct
    public void init() {
        DynamicDataSourceContextHolder.push("db-admin");
        refreshAllCache();
        DynamicDataSourceContextHolder.poll();
    }

    @Override
    public PageBean<StationWhitelistVo> listWhitelist(StationWhitelistQueryParam param) {
        PageHelper.startPage(param.getPageNum(), param.getPageSize());
        List<StationWhitelistVo> list = stationWhitelistMapper.selectJoinList(StationWhitelistVo.class,
                new com.github.yulichang.wrapper.MPJLambdaWrapper<StationWhitelist>()
                        .selectAll(StationWhitelist.class)
                        .select(User::getMobilePhone, User::getNickname)
                        .leftJoin(User.class, User::getId, StationWhitelist::getUserId)
                        .eq(!CommUtil.isEmptyOrNull(param.getStationId()), StationWhitelist::getStationId, param.getStationId())
                        .like(!CommUtil.isEmptyOrNull(param.getUserPhone()), User::getMobilePhone, param.getUserPhone())
                        .like(!CommUtil.isEmptyOrNull(param.getUserNickname()), User::getNickname, param.getUserNickname())
                        .eq(!CommUtil.isEmptyOrNull(param.getStatus()), StationWhitelist::getStatus, param.getStatus())
                        .orderByDesc(StationWhitelist::getCreateTime)
        );
        return new PageBean<>(list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addWhitelist(StationWhitelist whitelist) {
        CommUtil.assertsNonNull(whitelist.getStationId(), "站点ID不能为空");
        CommUtil.asserts(whitelist.getUserId() != null && whitelist.getUserId() > 0, "用户ID不能为空");

        Station station = stationService.lambdaQuery()
                .eq(Station::getStationId, whitelist.getStationId())
                .one();
        CommUtil.assertsNonNull(station, "站点不存在");

        User user = userService.getById(whitelist.getUserId());
        CommUtil.assertsNonNull(user, "用户不存在");

        StationWhitelist exists = lambdaQuery()
                .eq(StationWhitelist::getStationId, whitelist.getStationId())
                .eq(StationWhitelist::getUserId, whitelist.getUserId())
                .one();
        if (exists != null) {
            throw new BusinessException("用户已在该站点白名单中");
        }

        whitelist.setId(idGenerator.nextId());
        whitelist.setStationName(station.getStationName());
        whitelist.setUserPhone(user.getMobilePhone());
        whitelist.setUserNickname(user.getNickname());
        whitelist.setStatus(StationWhitelist.STATUS_启用);
        whitelist.setCreateBy(StpUtil.getLoginIdAsLong());

        save(whitelist);

        refreshCache(whitelist.getStationId());

        stationWhitelistLogService.logAdd(whitelist.getStationId(), whitelist.getStationName(),
                whitelist.getUserId(), whitelist.getUserPhone(),
                StpUtil.getLoginIdAsLong(), StpUtil.getSession().getString("mobilePhone"));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchAddWhitelist(BatchAddWhitelistDto dto) {
        CommUtil.assertsNonNull(dto.getStationId(), "站点ID不能为空");
        CommUtil.asserts(dto.getUserIds() != null && !dto.getUserIds().isEmpty(), "用户ID列表不能为空");

        Station station = stationService.lambdaQuery()
                .eq(Station::getStationId, dto.getStationId())
                .one();
        CommUtil.assertsNonNull(station, "站点不存在");

        List<StationWhitelist> existingList = lambdaQuery()
                .eq(StationWhitelist::getStationId, dto.getStationId())
                .in(StationWhitelist::getUserId, dto.getUserIds())
                .list();
        Set<Long> existingUserIds = existingList.stream()
                .map(StationWhitelist::getUserId)
                .collect(Collectors.toSet());

        List<User> users = userService.listByIds(dto.getUserIds());
        Map<Long, User> userMap = users.stream()
                .collect(Collectors.toMap(User::getId, u -> u));

        Long operatorId = StpUtil.getLoginIdAsLong();
        String operatorName = StpUtil.getSession().getString("mobilePhone");

        List<StationWhitelist> toSave = new ArrayList<>();
        for (Long userId : dto.getUserIds()) {
            if (existingUserIds.contains(userId)) {
                continue;
            }
            User user = userMap.get(userId);
            if (user == null) {
                continue;
            }
            StationWhitelist whitelist = new StationWhitelist();
            whitelist.setId(idGenerator.nextId());
            whitelist.setStationId(dto.getStationId());
            whitelist.setStationName(station.getStationName());
            whitelist.setUserId(userId);
            whitelist.setUserPhone(user.getMobilePhone());
            whitelist.setUserNickname(user.getNickname());
            whitelist.setStatus(StationWhitelist.STATUS_启用);
            whitelist.setRemark(dto.getRemark());
            whitelist.setCreateBy(operatorId);
            toSave.add(whitelist);
        }

        if (!toSave.isEmpty()) {
            saveBatch(toSave);
            refreshCache(dto.getStationId());
            stationWhitelistLogService.logBatchAdd(dto.getStationId(), station.getStationName(),
                    toSave.size(), operatorId, operatorName);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeWhitelist(Long id) {
        StationWhitelist whitelist = getById(id);
        CommUtil.assertsNonNull(whitelist, "白名单记录不存在");

        removeById(id);
        refreshCache(whitelist.getStationId());

        stationWhitelistLogService.logRemove(whitelist.getStationId(), whitelist.getStationName(),
                whitelist.getUserId(), whitelist.getUserPhone(),
                StpUtil.getLoginIdAsLong(), StpUtil.getSession().getString("mobilePhone"));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchRemoveWhitelist(List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return;
        }
        List<StationWhitelist> list = listByIds(ids);
        if (list.isEmpty()) {
            return;
        }

        removeByIds(ids);

        Map<String, List<StationWhitelist>> groupByStation = list.stream()
                .collect(Collectors.groupingBy(StationWhitelist::getStationId));
        for (String stationId : groupByStation.keySet()) {
            refreshCache(stationId);
        }

        stationWhitelistLogService.logBatchRemove(list.get(0).getStationId(), list.get(0).getStationName(),
                list.size(), StpUtil.getLoginIdAsLong(), StpUtil.getSession().getString("mobilePhone"));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(Long id, Integer status) {
        StationWhitelist whitelist = getById(id);
        CommUtil.assertsNonNull(whitelist, "白名单记录不存在");

        lambdaUpdate()
                .set(StationWhitelist::getStatus, status)
                .eq(StationWhitelist::getId, id)
                .update();

        refreshCache(whitelist.getStationId());

        stationWhitelistLogService.logStatusChange(whitelist.getStationId(), whitelist.getStationName(),
                whitelist.getUserId(), whitelist.getUserPhone(), status,
                StpUtil.getLoginIdAsLong(), StpUtil.getSession().getString("mobilePhone"));
    }

    @Override
    public boolean isInWhitelist(String stationId, Long userId) {
        String key = RedisKeys.STATION_WHITELIST_USERS + stationId;
        if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
            return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, String.valueOf(userId)));
        }
        
        // 缓存不存在，重建缓存
        refreshCache(stationId);
        
        // 再次检查缓存
        if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
            return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, String.valueOf(userId)));
        }
        
        return false;
    }

    @Override
    public List<Long> getWhitelistUserIds(String stationId) {
        String key = RedisKeys.STATION_WHITELIST_USERS + stationId;
        Set<String> members = redisTemplate.opsForSet().members(key);
        if (members != null && !members.isEmpty()) {
            return members.stream()
                    .map(Long::valueOf)
                    .collect(Collectors.toList());
        }
        List<StationWhitelist> list = lambdaQuery()
                .eq(StationWhitelist::getStationId, stationId)
                .eq(StationWhitelist::getStatus, StationWhitelist.STATUS_启用)
                .list();
        return list.stream()
                .map(StationWhitelist::getUserId)
                .collect(Collectors.toList());
    }

    @Override
    public void refreshCache(String stationId) {
        String key = RedisKeys.STATION_WHITELIST_USERS + stationId;
        redisTemplate.delete(key);

        List<StationWhitelist> list = lambdaQuery()
                .eq(StationWhitelist::getStationId, stationId)
                .eq(StationWhitelist::getStatus, StationWhitelist.STATUS_启用)
                .list();

        if (!list.isEmpty()) {
            String[] userIds = list.stream()
                    .map(w -> String.valueOf(w.getUserId()))
                    .toArray(String[]::new);
            redisTemplate.opsForSet().add(key, userIds);
            redisTemplate.expire(key, CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
        }
    }

    @Override
    public void refreshAllCache() {
        List<StationWhitelist> allList = lambdaQuery()
                .eq(StationWhitelist::getStatus, StationWhitelist.STATUS_启用)
                .list();

        Map<String, List<StationWhitelist>> groupByStation = allList.stream()
                .collect(Collectors.groupingBy(StationWhitelist::getStationId));

        for (Map.Entry<String, List<StationWhitelist>> entry : groupByStation.entrySet()) {
            String stationId = entry.getKey();
            List<StationWhitelist> list = entry.getValue();
            String key = RedisKeys.STATION_WHITELIST_USERS + stationId;
            redisTemplate.delete(key);
            String[] userIds = list.stream()
                    .map(w -> String.valueOf(w.getUserId()))
                    .toArray(String[]::new);
            redisTemplate.opsForSet().add(key, userIds);
            redisTemplate.expire(key, CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
        }
    }
}
