package com.emotion.service.impl;

import com.emotion.dto.request.LoginRequest;
import com.emotion.dto.request.RegisterRequest;
import com.emotion.dto.request.ResetPasswordRequest;
import com.emotion.dto.response.AuthResponse;
import com.emotion.dto.response.CaptchaResponse;
import com.emotion.dto.response.ResetPasswordResponse;
import com.emotion.dto.response.SmsCodeResponse;
import com.emotion.dto.response.UserInfoResponse;
import com.emotion.entity.User;
import com.emotion.exception.AuthException;
import com.emotion.exception.BusinessException;
import com.emotion.exception.CaptchaException;
import com.emotion.exception.TokenException;
import com.emotion.service.AuthService;
import com.emotion.service.UserService;
import com.emotion.util.JwtUtil;
import com.emotion.util.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 认证服务实现类
 *
 * @author huazhongmin
 * @date 2025-07-23
 */
@Slf4j
@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private TokenUtil tokenUtil;

    private static final String CAPTCHA_PREFIX = "captcha:";
    private static final String SMS_CODE_PREFIX = "sms_code:";
    private static final String TOKEN_PREFIX = "token:";
    private static final String REFRESH_TOKEN_PREFIX = "refresh_token:";
    private static final int CAPTCHA_EXPIRE_MINUTES = 5;
    private static final int SMS_CODE_EXPIRE_MINUTES = 5;
    private static final int TOKEN_EXPIRE_HOURS = 24;
    private static final int REFRESH_TOKEN_EXPIRE_DAYS = 7;
    private static final String DEFAULT_SMS_CODE = "123456";
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public AuthResponse login(LoginRequest request) {
        // 验证短信验证码
        if (!validateSmsCode(request.getPhone(), request.getSmsCode())) {
            throw new CaptchaException("验证码错误或已过期");
        }

        // 根据手机号查询用户
        User user = userService.getByPhone(request.getPhone());

        // 如果用户不存在，则自动注册
        if (user == null) {
            log.info("用户不存在，自动注册: phone={}", request.getPhone());
            user = autoRegisterUser(request.getPhone());
        } else {
            // 检查用户状态
            if (user.getStatus() != 1) {
                throw new AuthException("账号已被禁用");
            }
            log.info("用户登录: phone={}, userId={}", request.getPhone(), user.getId());
        }

        // 生成令牌
        String accessToken = generateAccessToken(user);
        String refreshToken = generateRefreshToken(user);

        // 更新用户最后活跃时间
        userService.updateLastActiveTime(user.getId(), LocalDateTime.now());

        // 构建响应
        AuthResponse response = new AuthResponse();
        response.setAccessToken(accessToken);
        response.setRefreshToken(refreshToken);
        response.setExpiresIn((long) TOKEN_EXPIRE_HOURS * 3600);
        response.setUserInfo(convertToUserInfoResponse(user));
        response.setLoginTime(LocalDateTime.now().format(DATE_TIME_FORMATTER));

        return response;
    }

    @Override
    public AuthResponse register(RegisterRequest request) {
        // 验证短信验证码
        if (!validateSmsCode(request.getPhone(), request.getSmsCode())) {
            throw new CaptchaException("验证码错误或已过期");
        }

        // 检查手机号是否已存在
        if (userService.getByPhone(request.getPhone()) != null) {
            throw new BusinessException("手机号已被注册");
        }

        // 生成随机用户名：开心 + 6位随机大小写字母和数字
        String username = generateRandomUsername();

        // 使用手机号作为账号
        String account = request.getPhone();

        // 创建用户（密码在UserService中加密，这里不需要预先加密）
        User user = userService.createUser(
                account,
                username,
                request.getPassword(),
                null,  // 邮箱为空
                request.getPhone()
        );

        // 生成令牌
        String accessToken = generateAccessToken(user);
        String refreshToken = generateRefreshToken(user);

        // 构建响应
        AuthResponse response = new AuthResponse();
        response.setAccessToken(accessToken);
        response.setRefreshToken(refreshToken);
        response.setExpiresIn((long) TOKEN_EXPIRE_HOURS * 3600);
        response.setUserInfo(convertToUserInfoResponse(user));
        response.setLoginTime(LocalDateTime.now().format(DATE_TIME_FORMATTER));

        log.info("用户注册成功: phone={}, username={}", request.getPhone(), username);
        return response;
    }

    @Override
    public ResetPasswordResponse resetPassword(ResetPasswordRequest request) {
        // 验证码本期固定为123456
        if (request.getCaptcha() == null || !"123456".equals(request.getCaptcha().trim())) {
            throw new CaptchaException("验证码错误或已过期");
        }

        // 根据手机号查询用户
        User user = userService.getByPhone(request.getPhone());
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        // 使用统一的 PasswordEncoder 进行加密，保持与登录/注册一致
        String encoded = passwordEncoder.encode(request.getNewPassword());
        user.setPassword(encoded);
        userService.updateById(user);

        ResetPasswordResponse resp = new ResetPasswordResponse();
        resp.setSuccess(true);
        resp.setMessage("重置密码成功");
        return resp;
    }

    @Override
    public UserInfoResponse getCurrentUserInfo(String userId) {
        User user = userService.getById(userId);
        if (user == null) {
            throw new AuthException("用户不存在");
        }
        return convertToUserInfoResponse(user);
    }

    @Override
    public CaptchaResponse generateCaptcha() {
        String captchaKey = UUID.randomUUID().toString();
        String captchaCode = generateCaptchaCode();

        // 生成验证码图片
        String captchaImage = generateCaptchaImage(captchaCode);

        // 存储验证码到Redis
        redisTemplate.opsForValue().set(
                CAPTCHA_PREFIX + captchaKey,
                        captchaCode.toLowerCase(),
                CAPTCHA_EXPIRE_MINUTES,
                TimeUnit.MINUTES
        );

        CaptchaResponse response = new CaptchaResponse();
        response.setCaptchaKey(captchaKey);
        response.setCaptchaImage(captchaImage);
        response.setExpiresIn((long) CAPTCHA_EXPIRE_MINUTES * 60);

        return response;
    }

    @Override
    public boolean validateCaptcha(String captchaKey, String captcha) {
        if (!StringUtils.hasText(captchaKey) || !StringUtils.hasText(captcha)) {
            return false;
        }

        String storedCaptcha = (String) redisTemplate.opsForValue().get(CAPTCHA_PREFIX + captchaKey);
        if (storedCaptcha == null) {
            return false;
        }

        // 验证成功后删除验证码
        redisTemplate.delete(CAPTCHA_PREFIX + captchaKey);

        return storedCaptcha.equalsIgnoreCase(captcha.trim());
    }

    @Override
    public boolean logout(String userId, String token) {
        // 删除访问令牌
        redisTemplate.delete(TOKEN_PREFIX + token);

        // 删除刷新令牌（如果存在）
        String refreshTokenKey = REFRESH_TOKEN_PREFIX + userId;
        redisTemplate.delete(refreshTokenKey);

        return true;
    }

    @Override
    public boolean logoutByToken(HttpServletRequest request) {
        String token = tokenUtil.extractToken(request);
        String userId = validateTokenAndGetUserId(token);
        return logout(userId, token);
    }

    /**
     * 验证令牌并获取用户ID
     */
    private String validateTokenAndGetUserId(String token) {
        if (!tokenUtil.isValidToken(token)) {
            throw new TokenException("未提供访问令牌");
        }

        if (!validateToken(token)) {
            throw new TokenException("访问令牌无效或已过期");
        }

        String userId = getUserIdFromToken(token);
        if (userId == null) {
            throw new TokenException("访问令牌无效");
        }

        return userId;
    }

    @Override
    public AuthResponse refreshToken(String refreshToken) {
        String userId = (String) redisTemplate.opsForValue().get(REFRESH_TOKEN_PREFIX + refreshToken);
        if (userId == null) {
            throw new TokenException("刷新令牌无效或已过期");
        }

        User user = userService.getById(userId);
        if (user == null) {
            throw new AuthException("用户不存在");
        }

        // 生成新的访问令牌
        String newAccessToken = generateAccessToken(user);
        String newRefreshToken = generateRefreshToken(user);

        // 删除旧的刷新令牌
        redisTemplate.delete(REFRESH_TOKEN_PREFIX + refreshToken);

        AuthResponse response = new AuthResponse();
        response.setAccessToken(newAccessToken);
        response.setRefreshToken(newRefreshToken);
        response.setExpiresIn((long) TOKEN_EXPIRE_HOURS * 3600);
        response.setUserInfo(convertToUserInfoResponse(user));
        response.setLoginTime(LocalDateTime.now().format(DATE_TIME_FORMATTER));

        return response;
    }

    @Override
    public boolean validateToken(HttpServletRequest request) {
        String token = tokenUtil.extractToken(request);
        return validateToken(token);
    }

    @Override
    public boolean validateToken(String token) {
        if (!tokenUtil.isValidToken(token)) {
            return false;
        }

        try {
            // 首先尝试JWT验证
            if (jwtUtil.validateToken(token)) {
                // JWT验证成功，再检查Redis中是否存在（用于登出功能）
                return redisTemplate.hasKey(TOKEN_PREFIX + token);
            }
            return false;
        } catch (Exception e) {
            log.warn("Token验证失败: {}", e.getMessage());
            return false;
        }
    }

    @Override
    public String getUserIdFromToken(String token) {
        if (!tokenUtil.isValidToken(token)) {
            return null;
        }

        try {
            // 首先尝试从JWT中获取用户ID
            String userId = jwtUtil.getUserIdFromToken(token);
            if (userId != null) {
                // 验证Redis中是否存在该token（确保未被登出）
                if (redisTemplate.hasKey(TOKEN_PREFIX + token)) {
                    return userId;
                }
            }
            return null;
        } catch (Exception e) {
            log.warn("从Token获取用户ID失败: {}", e.getMessage());
            return null;
        }
    }

    @Override
    public String getUsernameFromToken(String token) {
        if (!tokenUtil.isValidToken(token)) {
            return null;
        }

        try {
            // 直接从JWT中获取用户名
            return jwtUtil.getUsernameFromToken(token);
        } catch (Exception e) {
            log.warn("从Token获取用户名失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 生成访问令牌
     */
    private String generateAccessToken(User user) {
        // 使用JWT生成token
        String token = jwtUtil.generateToken(user.getId(), user.getUsername());

        // 同时在Redis中存储token信息，用于快速验证和登出功能
        redisTemplate.opsForValue().set(
                TOKEN_PREFIX + token,
                user.getId(),
                TOKEN_EXPIRE_HOURS,
                TimeUnit.HOURS
        );
        return token;
    }

    /**
     * 生成刷新令牌
     */
    private String generateRefreshToken(User user) {
        // 使用JWT生成刷新token
        String refreshToken = jwtUtil.generateRefreshToken(user.getId(), user.getUsername());

        // 在Redis中存储刷新token信息
        redisTemplate.opsForValue().set(
                REFRESH_TOKEN_PREFIX + refreshToken,
                user.getId(),
                REFRESH_TOKEN_EXPIRE_DAYS,
                TimeUnit.DAYS
        );
        return refreshToken;
    }

    /**
     * 生成验证码
     */
    private String generateCaptchaCode() {
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < 4; i++) {
            code.append(random.nextInt(10));
        }
        return code.toString();
    }

    /**
     * 生成验证码图片
     */
    private String generateCaptchaImage(String code) {
        int width = 120;
        int height = 40;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();

        // 设置背景色
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);

        // 设置字体
        g.setFont(new Font("Arial", Font.BOLD, 20));
        g.setColor(Color.BLACK);

        // 绘制验证码
        for (int i = 0; i < code.length(); i++) {
            g.drawString(String.valueOf(code.charAt(i)), 20 + i * 20, 25);
        }

        // 添加干扰线
        Random random = new Random();
        g.setColor(Color.GRAY);
        for (int i = 0; i < 5; i++) {
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            g.drawLine(x1, y1, x2, y2);
        }

        g.dispose();

        // 转换为Base64
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(image, "png", baos);
            byte[] imageBytes = baos.toByteArray();
            return "data:image/png;base64," + Base64.getEncoder().encodeToString(imageBytes);
        } catch (IOException e) {
            throw new CaptchaException("生成验证码图片失败");
        }
    }

    /**
     * 验证密码
     */
    private boolean verifyPassword(String rawPassword, String encodedPassword) {
        // 使用BCrypt进行密码验证
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }

    /**
     * 加密密码
     */
    private String encryptPassword(String rawPassword) {
        // 使用BCrypt进行密码加密
        return passwordEncoder.encode(rawPassword);
    }

    /**
     * 转换为用户信息响应
     */
    private UserInfoResponse convertToUserInfoResponse(User user) {
        UserInfoResponse response = new UserInfoResponse();
        BeanUtils.copyProperties(user, response);
        if (user.getCreateTime() != null) {
            response.setCreateTime(user.getCreateTime().format(DATE_TIME_FORMATTER));
        }
        if (user.getLastActiveTime() != null) {
            response.setLastActiveTime(user.getLastActiveTime().format(DATE_TIME_FORMATTER));
        }
        return response;
    }

    @Override
    public boolean existsByAccount(String account) {
        if (!StringUtils.hasText(account)) {
            return false;
        }
        User user = userService.getByAccount(account);
        return user != null;
    }

    @Override
    public boolean existsByEmail(String email) {
        if (!StringUtils.hasText(email)) {
            return false;
        }
        User user = userService.getByEmail(email);
        return user != null;
    }

    @Override
    public boolean existsByPhone(String phone) {
        if (!StringUtils.hasText(phone)) {
            return false;
        }
        User user = userService.getByPhone(phone);
        return user != null;
    }

    @Override
    public SmsCodeResponse sendSmsCode(String phone) {
        // 验证手机号格式
        if (!StringUtils.hasText(phone) || !phone.matches("^1[3-9]\\d{9}$")) {
            throw new BusinessException("手机号格式不正确");
        }

        // 检查手机号是否已注册，用于提示信息
        boolean isRegistered = existsByPhone(phone);
        String message;
        if (isRegistered) {
            message = "验证码已发送，用于登录验证，有效期" + SMS_CODE_EXPIRE_MINUTES + "分钟";
        } else {
            message = "验证码已发送，用于注册验证，有效期" + SMS_CODE_EXPIRE_MINUTES + "分钟";
        }

        // TODO: 接入真实的短信服务商（阿里云、腾讯云等）
        // 目前使用固定验证码 123456
        String code = DEFAULT_SMS_CODE;

        // 将验证码存储到Redis，有效期5分钟
        String key = SMS_CODE_PREFIX + phone;
        redisTemplate.opsForValue().set(key, code, SMS_CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);

        log.info("发送短信验证码: phone={}, code={}, isRegistered={}", phone, code, isRegistered);

        // 构建响应
        return SmsCodeResponse.builder()
                .code(code)  // 开发环境返回验证码，生产环境应该删除此行
                .expiresIn((long) SMS_CODE_EXPIRE_MINUTES * 60)
                .message(message)
                .build();
    }

    @Override
    public boolean validateSmsCode(String phone, String code) {
        if (!StringUtils.hasText(phone) || !StringUtils.hasText(code)) {
            return false;
        }

        String key = SMS_CODE_PREFIX + phone;
        String storedCode = (String) redisTemplate.opsForValue().get(key);

        if (storedCode == null) {
            log.warn("短信验证码不存在或已过期: phone={}", phone);
            return false;
        }

        boolean isValid = storedCode.equals(code);

        // 验证成功后删除验证码（一次性使用）
        if (isValid) {
            redisTemplate.delete(key);
            log.info("短信验证码验证成功: phone={}", phone);
        } else {
            log.warn("短信验证码错误: phone={}, expected={}, actual={}", phone, storedCode, code);
        }

        return isValid;
    }

    /**
     * 生成随机用户名
     * 格式：开心 + 6位随机大小写字母和数字
     *
     * @return 随机用户名
     */
    private String generateRandomUsername() {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder("开心");

        for (int i = 0; i < 6; i++) {
            int index = random.nextInt(chars.length());
            sb.append(chars.charAt(index));
        }

        return sb.toString();
    }

    /**
     * 自动注册用户（用于登录时用户不存在的情况）
     *
     * @param phone 手机号
     * @return 新创建的用户
     */
    private User autoRegisterUser(String phone) {
        // 生成随机用户名：开心 + 6位随机大小写字母和数字
        String username = generateRandomUsername();

        // 使用手机号作为账号
        String account = phone;

        // 生成随机密码（用户可以后续修改）
        String randomPassword = generateRandomPassword();

        // 创建用户
        User user = userService.createUser(
                account,
                username,
                randomPassword,
                null,  // 邮箱为空
                phone
        );

        log.info("自动注册用户成功: phone={}, username={}, userId={}", phone, username, user.getId());
        return user;
    }

    /**
     * 生成随机密码
     * 格式：8位随机大小写字母和数字
     *
     * @return 随机密码
     */
    private String generateRandomPassword() {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < 8; i++) {
            int index = random.nextInt(chars.length());
            sb.append(chars.charAt(index));
        }

        return sb.toString();
    }
}