package com.corewing.app.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.corewing.app.common.Result;
import com.corewing.app.common.page.PageContext;
import com.corewing.app.dto.CodeLoginRequest;
import com.corewing.app.dto.ForgetPasswordRequest;
import com.corewing.app.dto.LogoffRequest;
import com.corewing.app.entity.User;
import com.corewing.app.mapper.UserMapper;
import com.corewing.app.service.UserService;
import com.corewing.app.service.VerifyCodeService;
import com.corewing.app.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 应用用户 Service 实现类
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private VerifyCodeService verifyCodeService;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private SmsBaoUtil smsBaoUtil;

    @Resource
    private EmailUtil emailUtil;


    @Override
    public Page<User> page(User user) {
        Page<User> page = PageContext.getDefaultPage(User.class);
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.hasText(user.getNickName()), User::getNickName, user.getNickName());
        queryWrapper.like(StringUtils.hasText(user.getUsername()), User::getUsername, user.getUsername());
        queryWrapper.like(user.getStatus() != null, User::getStatus, user.getStatus());
        return page(page, queryWrapper);
    }

    @Override
    public boolean save(User user) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, user.getUsername());
        if(count(queryWrapper) > 0) {
            throw new RuntimeException(I18nUtil.getMessage(I18nUtil.getMessage("error.username.exists")));
        }


        return super.save(user);
    }

    @Override
    public boolean update(User user) {

        // 校验用户名
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, user.getUsername());
        queryWrapper.ne(User::getId, user.getId());
        if(count(queryWrapper) > 0) {
            throw new RuntimeException(I18nUtil.getMessage(I18nUtil.getMessage("error.username.exists")));
        }

        // 校验手机号码
        LambdaQueryWrapper<User> checkPhoneWrapper = new LambdaQueryWrapper<>();
        checkPhoneWrapper.eq(User::getUsername, user.getUsername());
        checkPhoneWrapper.ne(User::getId, user.getId());
        if(count(checkPhoneWrapper) > 0) {
            throw new RuntimeException(I18nUtil.getMessage(I18nUtil.getMessage("error.phone.exists")));
        }

        // 校验邮箱
        LambdaQueryWrapper<User> checkEmailWrapper = new LambdaQueryWrapper<>();
        checkEmailWrapper.eq(User::getUsername, user.getUsername());
        checkEmailWrapper.ne(User::getId, user.getId());
        if(count(checkEmailWrapper) > 0) {
            throw new RuntimeException(I18nUtil.getMessage(I18nUtil.getMessage("error.email.exists")));
        }

        return updateById(user);
    }

    @Override
    public User getByUsername(String username) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, username);
        return this.getOne(wrapper);
    }

    @Override
    public User getByAccount(String account) {
        if (!StringUtils.hasText(account)) {
            return null;
        }

        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, account)
                .or()
                .eq(User::getEmail, account)
                .or()
                .eq(User::getTelephone, account);
        return this.getOne(wrapper);
    }

    @Override
    public User getByEmail(String email) {
        if (!StringUtils.hasText(email)) {
            return null;
        }
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getEmail, email);
        return this.getOne(wrapper);
    }

    @Override
    public User getByTelephone(String telephone) {
        if (!StringUtils.hasText(telephone)) {
            return null;
        }
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getTelephone, telephone);
        return this.getOne(wrapper);
    }

    @Override
    public String login(String account, String password) {
        // 查询用户（支持用户名/邮箱/手机号）
        User user = getByAccount(account);
        if (user == null) {
            throw new RuntimeException(I18nUtil.getMessage("error.user.not.found"));
        }

        // 验证密码（MD5加密）
        String encryptPassword = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));
        // 客户端已经使用加密
        if (!encryptPassword.equals(user.getPassword())) {
            throw new RuntimeException(I18nUtil.getMessage("error.password.incorrect"));
        }

        // 检查用户状态
        if (user.getStatus() == 0) {
            throw new RuntimeException(I18nUtil.getMessage("error.account.disabled"));
        }

        // 登录成功，使用 Sa-Token 生成 token
        StpUtil.login(user.getId());
        return StpUtil.getTokenValue();
    }

    @Override
    public boolean register(User user, String code, String registerIp) {
        // 检查用户名是否已存在
        User existUser = getByUsername(user.getUsername());
        if (existUser != null) {
            throw new RuntimeException(I18nUtil.getMessage("error.username.exists"));
        }

        // 邮箱和手机号至少要有一个
        if (!StringUtils.hasText(user.getEmail()) && !StringUtils.hasText(user.getTelephone())) {
            throw new RuntimeException(I18nUtil.getMessage("error.email.or.phone.required"));
        }

        // 检查邮箱是否已存在
        if (StringUtils.hasText(user.getEmail())) {
            existUser = getByEmail(user.getEmail());
            if (existUser != null) {
                throw new RuntimeException(I18nUtil.getMessage("error.email.exists"));
            }
        }

        // 检查手机号是否已存在
        if (StringUtils.hasText(user.getTelephone())) {
            existUser = getByTelephone(user.getTelephone());
            if (existUser != null) {
                throw new RuntimeException(I18nUtil.getMessage("error.phone.exists"));
            }
        }

        // 验证验证码
        String account = StringUtils.hasText(user.getTelephone()) ? user.getTelephone() : user.getEmail();
        boolean codeValid = verifyCodeService.verifyCode(account, code, "register");
        if (!codeValid) {
            throw new RuntimeException(I18nUtil.getMessage("error.verify.code.invalid"));
        }

        // 密码加密（MD5）
        String encryptPassword = DigestUtils.md5DigestAsHex(user.getPassword().getBytes(StandardCharsets.UTF_8));
        user.setPassword(encryptPassword);

        // 设置默认状态为启用
        if (user.getStatus() == null) {
            user.setStatus(1);
        }

        // 设置注册IP和归属地
        if (StringUtils.hasText(registerIp)) {
            user.setRegisterIp(registerIp);
            user.setRegisterRegion(CommonIpAddressUtil.getCityInfo(registerIp));
        }

        // 保存用户
        return this.save(user);
    }

    @Override
    public void updateLoginIp(Long userId, String loginIp) {
        User user = new User();
        user.setId(userId);
        user.setLoginIp(loginIp);
        this.updateById(user);
    }

    @Override
    public void updateLoginInfo(Long userId, String loginIp) {
        User user = new User();
        user.setId(userId);
        user.setLoginIp(loginIp);
        user.setLoginRegion(CommonIpAddressUtil.getCityInfo(loginIp));
        this.updateById(user);
    }



    @Override
    public boolean forgetPassword(ForgetPasswordRequest request) {
        User user = getByAccount(request.getAccount());
        if(user == null) {
            throw new RuntimeException(I18nUtil.getMessage("error.user.not.found"));
        }

        String codeKey = String.format("verify_code:%s:%s", "forget", request.getAccount());
        Object checkCode = redisUtil.get(codeKey);

        if(checkCode == null || !request.getVerificationCode().equalsIgnoreCase(checkCode.toString())) {
            throw new RuntimeException(I18nUtil.getMessage("error.verify.code.invalid"));
        }

        String encryptPassword = DigestUtils.md5DigestAsHex(request.getPassword().getBytes(StandardCharsets.UTF_8));
        user.setPassword(encryptPassword);
        return updateById(user);
    }

    @Override
    public String codeLogin(CodeLoginRequest request) {

        // 校验验证码是否正确
        String codeKey = String.format("verify_code:%s:%s", "login", request.getAccount());
        Object checkCode = redisUtil.get(codeKey);
        if(checkCode == null || !request.getVerificationCode().equalsIgnoreCase(checkCode.toString())) {
            throw new RuntimeException(I18nUtil.getMessage("error.verify.code.invalid"));
        }

        /// 校验账号是否存在
        User user = getByAccount(request.getAccount());
        if(user == null) {
            throw new RuntimeException(I18nUtil.getMessage("error.user.not.found"));
        }
        StpUtil.login(user.getId());
        return StpUtil.getTokenValue();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean logoff(LogoffRequest request) {
        User user = this.getById(StpUtil.getLoginId().toString());
        // 校验验证码
        if(user == null) {
            throw new RuntimeException(I18nUtil.getMessage("error.user.not.found"));
        }
        String codeKey = String.format("verify_code:%s:%s", "logoff", user.getUsername());
        Object checkCode = redisUtil.get(codeKey);

        if(checkCode == null || !request.getCode().equalsIgnoreCase(checkCode.toString())) {
            throw new RuntimeException(I18nUtil.getMessage("error.verify.code.invalid"));
        }
        StpUtil.logout();
        DevLogUtil.executeLogoutLog(user.getUsername());
        // 删除记录
        this.removeById(user.getId());
        return true;
    }

    @Override
    public Result<Map<String, String>> deleteSendCode() {
        User user = this.getById(StpUtil.getLoginId().toString());
        if(user == null) {
            throw new RuntimeException(I18nUtil.getMessage("error.user.not.found"));
        }

        String code = CommonGenerateUtil.generateCode();

        // 保存到 Redis，key格式：verify_code:{type}:{account}
        String redisKey = String.format("verify_code:%s:%s", "logoff", user.getUsername());
        redisUtil.set(redisKey, code, CommonGenerateUtil.CODE_EXPIRE_MINUTES * 60);

        log.info("验证码已生成: account={}, type={}, code={}", user.getUsername(), "logoff", code);

        // 发送验证码
        if (StringUtils.hasText(user.getTelephone())) {

            // 获取当前ip
            String clientIp = IpUtil.getClientIp(CommonServletUtil.getRequest());
            clientIp = clientIp.replaceAll(":", "");
            // 使用 setIfAbsent 初始化计数器（只在键不存在时创建，并设置过期时间为1天）
            boolean isNew = redisUtil.setIfAbsent("exceed_" + clientIp, 1, 1, TimeUnit.DAYS);
            // 如果键已存在，递增计数器
            long sendCount = isNew ? 1 : redisUtil.incr("exceed_" + clientIp, 1);

            if (sendCount <= 4) {

                // 发送短信验证码
                boolean success = smsBaoUtil.sendVerifyCode(user.getTelephone(), code);
                if (!success) {
                    throw new RuntimeException(I18nUtil.getMessage("error.sms.send.failed"));
                }
                Map<String, String> map = new HashMap<>();
                map.put("account", user.getTelephone());
                return Result.success(map);
            }
            throw new RuntimeException(I18nUtil.getMessage("error.sms.send.exceed"));
        } else {
            // 发送邮件验证码
            boolean success = emailUtil.sendVerifyCode(user.getEmail(), code);
            if (!success) {
                throw new RuntimeException(I18nUtil.getMessage("error.email.send.failed"));
            }
            Map<String, String> map = new HashMap<>();
            map.put("account", user.getEmail());
            return Result.success(map);
        }
    }
}
