package shop.alien.gateway.service;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import shop.alien.entity.result.R;
import shop.alien.entity.second.LifeUserLog;
import shop.alien.entity.second.SecondRiskControlRecord;
import shop.alien.entity.second.SecondUserCredit;
import shop.alien.entity.second.SecondUserCreditRecord;
import shop.alien.entity.second.enums.SecondUserCreditScoreEnum;
import shop.alien.entity.store.LifeUser;
import shop.alien.entity.store.StoreImg;
import shop.alien.entity.store.StoreUser;
import shop.alien.entity.store.vo.LifeUserVo;
import shop.alien.gateway.config.BaseRedisService;
import shop.alien.gateway.config.RiskControlProperties;
import shop.alien.gateway.feign.SecondServiceFeign;
import shop.alien.gateway.mapper.LifeUserLogGatewayMapper;
import shop.alien.gateway.mapper.LifeUserGatewayMapper;
import shop.alien.mapper.second.SecondRiskControlRecordMapper;
import shop.alien.mapper.second.SecondUserCreditMapper;
import shop.alien.mapper.second.SecondUserCreditRecordMapper;
import shop.alien.util.common.JwtUtil;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户
 */
@Service
@RequiredArgsConstructor
public class LifeUserService extends ServiceImpl<LifeUserGatewayMapper, LifeUser> {

    private final LifeUserGatewayMapper lifeUserMapper;

    private final LifeUserLogGatewayMapper lifeUserLogMapper;

    private final BaseRedisService baseRedisService;

    private final SecondServiceFeign alienSecondFeign;

    private final ActivityInviteConfigService activityInviteConfigService;

    private final SecondRiskControlRecordMapper secondRiskControlRecordMapper;

    private final LifeUserLogTransactionService lifeUserLogTransactionService;

    @Autowired
    private final SecondUserCreditMapper secondUserCreditMapper;

    @Autowired
    private final SecondUserCreditRecordMapper secondUserCreditRecordMapper;

    @Autowired
    private RiskControlProperties riskControlProperties;

    @Value("${jwt.expiration-time}")
    private String effectiveTime;

    private static final DateTimeFormatter LOGOUT_END_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy/MM/dd");
    private static final DateTimeFormatter RE_REGISTER_ALLOW_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");

    /**
     * 申请注销结束时间：logoutTime 起算第 7 天（T+0～T+7），与定时任务冷静期一致。
     */
    public static String resolveLogoutEndTime(Date logoutTime, Integer logoutFlag) {
        if (logoutTime == null || logoutFlag == null || logoutFlag == 0) {
            return null;
        }
        return logoutTime.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate()
                .plusDays(7)
                .format(LOGOUT_END_TIME_FORMAT);
    }

    public void fillLogoutEndTime(LifeUserVo userVo, LifeUser user) {
        if (userVo == null || user == null) {
            return;
        }
        userVo.setLogoutEndTime(resolveLogoutEndTime(user.getLogoutTime(), user.getLogoutFlag()));
    }

    /**
     * 注销完成日 T（logoutTime）起，至 T+{@link LifeUser#RE_REGISTER_BLOCK_DAYS} 日 00:00 前禁止重新注册/登录。
     */
    public static boolean isWithinReRegisterBlockPeriod(Date logoutTime) {
        if (logoutTime == null) {
            return false;
        }
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime allowFrom = logoutTime.toInstant()
                .atZone(zone)
                .toLocalDate()
                .plusDays(LifeUser.RE_REGISTER_BLOCK_DAYS)
                .atStartOfDay();
        return LocalDateTime.now(zone).isBefore(allowFrom);
    }

    /**
     * 可重新注册时间：logoutTime 对应日 T 的 T+14 日 00:00。
     */
    public static String resolveReRegisterAllowTime(Date logoutTime) {
        if (logoutTime == null) {
            return null;
        }
        return logoutTime.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate()
                .plusDays(LifeUser.RE_REGISTER_BLOCK_DAYS)
                .atStartOfDay()
                .format(RE_REGISTER_ALLOW_TIME_FORMAT);
    }

    /**
     * 手机号处于注销禁登期时返回完整提示，否则返回 null。
     */
    public String getReRegisterBlockedMessage(String phoneNum) {
        LifeUser user = getUserByPhoneDelete(phoneNum);
        if (user == null
                || LifeUser.LOGOUT_FLAG_DONE != user.getLogoutFlag()
                || !isWithinReRegisterBlockPeriod(user.getLogoutTime())) {
            return null;
        }
        String allowTime = resolveReRegisterAllowTime(user.getLogoutTime());
        if (allowTime == null) {
            return null;
        }
        return "原手机号无法注册新账号，" + allowTime + "起，原手机号可重新注册";
    }

    public LifeUserVo userLogin(String phoneNum, String inviteCode, String macIp) {
        LifeUser user = getUserByPhoneNew(phoneNum);
        if (user == null) {
            LifeUser lifeUser = new LifeUser();
            lifeUser.setUserPhone(phoneNum);
            lifeUser.setUserName(phoneNum);
            lifeUser.setRealName(phoneNum);
            lifeUser.setLogoutFlag(LifeUser.LOGOUT_FLAG_NORMAL);
            lifeUser.setCreatedTime(new Date());
            int ret = lifeUserMapper.insert(lifeUser);
            if (ret == 1) {
                LifeUser user2 = lifeUser.getId() != null ? lifeUser : getUserByPhoneNew(phoneNum);
                if (user2 == null) {
                    return null;
                }
                LifeUserVo userVo = new LifeUserVo();
                BeanUtils.copyProperties(user2, userVo);
                Map<String, String> tokenMap = new HashMap<>();
                tokenMap.put("phone", phoneNum);
                tokenMap.put("userName", lifeUser.getUserName());
                tokenMap.put("userId", lifeUser.getId().toString());
                tokenMap.put("userType", "user");
//                userVo.setToken(JWTUtils1.createToken(tokenMap));
                String token = getToken(phoneNum, userVo.getUserName(), tokenMap);
                userVo.setToken(token);
                addLifeUserSessionToken(phoneNum, token);
                // 二手平台登录log，同一个macip登录多账号记录
                addLifeUserLogInfo(user2, macIp);
                userVo.setHasLoginPassword(StringUtils.isNotBlank(user2.getPassword()));
                fillLogoutEndTime(userVo, user2);

                return userVo;
            } else {
                return null;
            }
        } else {
            LifeUserVo userVo = new LifeUserVo();
            BeanUtils.copyProperties(user, userVo);
            Map<String, String> tokenMap = new HashMap<>();
            tokenMap.put("phone", phoneNum);
            tokenMap.put("userName", user.getUserName());
            tokenMap.put("userId", user.getId().toString());
            tokenMap.put("userType", "user");
            String token = getToken(phoneNum, user.getUserName(), tokenMap);
            userVo.setToken(token);
            addLifeUserSessionToken(phoneNum, token);
            // 二手平台登录log，同一个macip登录多账号记录
            addLifeUserLogInfo(user, macIp);
            userVo.setHasLoginPassword(StringUtils.isNotBlank(user.getPassword()));
            fillLogoutEndTime(userVo, user);

            return userVo;
        }
    }

    private String getToken(String phoneNum, String userVo, Map<String, String> tokenMap) {
        int effectiveTimeInt = Integer.parseInt(effectiveTime.substring(0, effectiveTime.length() - 1));
        String effectiveTimeUnit = effectiveTime.substring(effectiveTime.length() - 1);
        long effectiveTimeIntLong = 0L;
        switch (effectiveTimeUnit) {
            case "s": {
                effectiveTimeIntLong = effectiveTimeInt * 1000L;
                break;
            }
            case "m": {
                effectiveTimeIntLong = effectiveTimeInt * 60L * 1000L;
                break;
            }
            case "h": {
                effectiveTimeIntLong = effectiveTimeInt * 60L * 60L * 1000L;
                break;
            }
            case "d": {
                effectiveTimeIntLong = effectiveTimeInt * 24L * 60L * 60L * 1000L;
                break;
            }
        }
        String token = JwtUtil.createJWT("user_" + phoneNum, userVo, JSONObject.toJSONString(tokenMap), effectiveTimeIntLong);
        return token;
    }

    /**
     * C 端多设备：token 放入 Redis Set；将旧版单 key 迁入 Set 后删除，避免旧 token 长期有效。
     */
    private void addLifeUserSessionToken(String phone, String token) {
        String legacyKey = "user_" + phone;
        String sessionSetKey = "user_sessions:" + phone;
        String oldSingle = baseRedisService.getString(legacyKey);
        if (oldSingle != null && !oldSingle.isEmpty()) {
            baseRedisService.setSetList(sessionSetKey, oldSingle);
        }
        baseRedisService.delete(legacyKey);
        baseRedisService.setSetList(sessionSetKey, token);
    }

    public LifeUser getUserByPhone(String phoneNum) {
        return findLatestActiveUserByPhone(phoneNum);
    }

    public LifeUser getUserByPhoneNew(String phoneNum) {
        return findLatestActiveUserByPhone(phoneNum);
    }

    /**
     * 按手机号取最新可用用户：未注销或冷静期；logout_flag 为空视为未注销（历史数据）
     */
    private LifeUser findLatestActiveUserByPhone(String phoneNum) {
        LambdaQueryWrapper<LifeUser> w = new LambdaQueryWrapper<>();
        w.eq(LifeUser::getUserPhone, phoneNum);
        w.and(x -> x.isNull(LifeUser::getLogoutFlag)
                .or().eq(LifeUser::getLogoutFlag, LifeUser.LOGOUT_FLAG_NORMAL)
                .or().eq(LifeUser::getLogoutFlag, LifeUser.LOGOUT_FLAG_COOLING));
        w.orderByDesc(LifeUser::getCreatedTime).orderByDesc(LifeUser::getId);
        w.last("LIMIT 1");
        List<LifeUser> list = this.list(w);
        return list.isEmpty() ? null : list.get(0);
    }

    public LifeUser getUserByPhoneDelete(String phoneNum) {
        return lifeUserMapper.selectCancelledDeletedByPhone(phoneNum);
    }
    public R<String> saveDeviceId(Integer id, String deviceId) {
        if (id == null || id <= 0) {
            return R.fail("用户id不能为空");
        }
        String normalized = normalizeDeviceId(deviceId);
        if (StringUtils.isBlank(normalized)) {
            return R.fail("deviceId不能为空");
        }
        LifeUser user = lifeUserMapper.selectById(id);
        if (user == null) {
            return R.fail("用户不存在");
        }
        if (normalized.equals(user.getDeviceId())) {
            return R.success("保存成功");
        }
        LifeUser update = new LifeUser();
        update.setId(id);
        //当原来的字段有值,在后面用逗号拼接
//        update.setDeviceId(user.getDeviceId() == null ? normalized : user.getDeviceId() + "," + normalized);
        update.setDeviceId(normalized);
        return lifeUserMapper.updateById(update) > 0 ? R.success("保存成功") : R.fail("保存失败");
    }

    private String normalizeDeviceId(String deviceId) {
        if (StringUtils.isBlank(deviceId)) {
            return null;
        }
        String trimmed = deviceId.trim();
        return trimmed.length() > 500 ? trimmed.substring(0, 500) : trimmed;
    }


    /**
     * 用户登录log存放（加入mac地址）
     * 记录用户登录信息并进行风控检查
     */
    @Transactional
    public void addLifeUserLogInfo(LifeUser user, String macIp) {
        try {
            LifeUserLog lifeUserLog = new LifeUserLog();
            lifeUserLog.setUserId(user.getId());
            lifeUserLog.setUserName(user.getUserName());
            lifeUserLog.setMacIp(macIp);
            lifeUserLog.setCreatedTime(new Date());
            // 通过独立服务类调用，确保事务代理生效，插入操作立即提交
            int count = lifeUserLogTransactionService.insertLifeUserLog(lifeUserLog);
            if (count > 0) {
                String startDate = LocalDateTime.now().minusHours(24L).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                String endDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                // 通过独立服务类查询，确保能看到刚才插入并提交的数据
                List<LifeUserLog> lsit = lifeUserLogTransactionService.getLifeUserLogByDate(startDate, endDate, macIp);

                if (lsit.size() > riskControlProperties.getAccountAbnormal().getRegCount24h() && !isViolation(startDate, endDate, macIp, user.getId())) {

                    for (LifeUserLog row: lsit) {
                        List<SecondUserCreditRecord> num = secondUserCreditRecordMapper.selectList(new LambdaQueryWrapper<SecondUserCreditRecord>()
                                .eq(SecondUserCreditRecord::getUserId, row.getUserId())
                                .eq(SecondUserCreditRecord::getPointsType, SecondUserCreditScoreEnum.ABNORMAL_ACCOUNTV.getCode())
                                .ge(SecondUserCreditRecord::getCreatedTime, startDate)
                                .le(SecondUserCreditRecord::getCreatedTime, endDate));

                        if (num.size() <= 0) {
                            LambdaQueryWrapper<SecondUserCredit> queryWrapper = new LambdaQueryWrapper<>();
                            queryWrapper.eq(SecondUserCredit::getUserId, row.getUserId())
                                    .orderByDesc(SecondUserCredit::getCreatedTime).last("LIMIT 1");
                            SecondUserCredit secondUserCredit = secondUserCreditMapper.selectOne(queryWrapper);
                            int score = secondUserCredit.getUserPoints() + SecondUserCreditScoreEnum.ABNORMAL_ACCOUNTV.getScore();
                            secondUserCredit.setUserPoints(score);
                            secondUserCreditMapper.updateById(secondUserCredit);

                            SecondUserCreditRecord record = new SecondUserCreditRecord();
                            record.setUserId(row.getUserId());
                            record.setPointsType(SecondUserCreditScoreEnum.ABNORMAL_ACCOUNTV.getCode());
                            record.setPoints(SecondUserCreditScoreEnum.ABNORMAL_ACCOUNTV.getScore());
                            record.setCurrentScoreCount(score);
                            record.setCreatedTime(new Date());
                            secondUserCreditRecordMapper.insert(record);

                            // 小于100分封禁用户
                            if (score < 100) {
                                LifeUser user1 = new LifeUser();
                                user1.setId(row.getUserId());
                                user1.setIsBanned(1);
                                lifeUserMapper.updateById(user1);
                            }
                        }
                    }

//                    String detailInfo = lsit.stream()
//                            .map(row -> row.getUserId().toString())
//                            .collect(Collectors.joining(","));
//                    alienSecondFeign.recordRiskControlData(user.getId(), 2, "账号异常", macIp, detailInfo);
                }
            }
            // 第一次登录，添加用户基础积分
            alienSecondFeign.createPointsRecord(user.getId(), 300, 1);
        } catch (Exception e) {
            log.error("用户登录log存放异常：{}", e);
        }

    }

    /**
     * 判断是否已存在违规记录
     * 检查指定时间段内是否已经记录过该用户的风控信息
     *
     * @param startDate 开始时间
     * @param endDate 结束时间
     * @param macIp MAC地址/IP
     * @param userId 用户ID
     * @return boolean true-已存在违规记录，false-不存在
     */
    public boolean isViolation(String startDate, String endDate, String macIp, Integer userId) {
        List<SecondRiskControlRecord> list = secondRiskControlRecordMapper.selectByBusinessId(startDate, endDate, macIp);
        for (SecondRiskControlRecord record : list) {
            // 将数组转换为包含整数的列表
            List<Integer> userIdList = Arrays.stream(record.getDetailInfo().split(","))
                    .map(Integer::parseInt)
                    .collect(Collectors.toList());
            if (userIdList.contains(userId)) {
                return true;
            }
        }
        return false;
    }
}
