/**
 * 自然写互动课堂教学管理云平台软件 V1.0
 * 
 * 用户与权限服务
 * 实现 RBAC 角色权限模型，管理教师/学生/管理员/家长四级权限
 */
package com.writech.cloud.service;

import com.writech.cloud.model.User;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 用户服务类
 * 
 * 提供用户管理、身份验证、权限控制、Token管理等核心功能
 * RBAC权限模型：管理员 > 教师 > 学生/家长
 * - 管理员：系统全局管理（学校/用户/设备管理）
 * - 教师：班级管理、作业发布批改、学情查看
 * - 学生：作业查看、学习数据查询
 * - 家长：子女学情查看、消息接收
 */
@Service
public class UserService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /** 密码加密器（BCrypt算法，强度因子10） */
    private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(10);

    /** Token黑名单前缀（存储在Redis中） */
    private static final String TOKEN_BLACKLIST_PREFIX = "writech:token:blacklist:";

    /** 短信验证码前缀 */
    private static final String SMS_CODE_PREFIX = "writech:sms:code:";

    /** 验证码有效期（秒） */
    private static final long SMS_CODE_EXPIRE = 300;

    /** 验证码发送间隔（秒） */
    private static final long SMS_CODE_INTERVAL = 60;

    /**
     * 手机号+密码验证登录
     * 
     * @param phone 手机号
     * @param password 明文密码
     * @return 验证通过返回用户对象，失败返回null
     */
    public User verifyByPassword(String phone, String password) {
        if (phone == null || password == null) {
            return null;
        }

        // 查询用户（手机号AES解密后匹配）
        User user = findByPhone(phone);
        if (user == null) {
            return null;
        }

        // BCrypt密码比对
        if (passwordEncoder.matches(password, user.getPasswordHash())) {
            return user;
        }

        // 登录失败计数（防暴力破解，5次失败后锁定30分钟）
        incrementLoginFailCount(user.getId());
        return null;
    }

    /**
     * 手机号+短信验证码验证登录
     */
    public User verifyBySmsCode(String phone, String smsCode) {
        if (phone == null || smsCode == null) {
            return null;
        }

        // 从Redis获取验证码
        String key = SMS_CODE_PREFIX + phone;
        String storedCode = redisTemplate.opsForValue().get(key);

        if (storedCode == null || !storedCode.equals(smsCode)) {
            return null;
        }

        // 验证码匹配成功，删除已使用的验证码
        redisTemplate.delete(key);

        // 查找或自动注册用户
        User user = findByPhone(phone);
        if (user == null) {
            // 首次登录自动创建账户
            user = autoRegister(phone);
        }

        return user;
    }

    /**
     * 微信授权登录验证
     */
    public User verifyByWechat(String wechatCode) {
        if (wechatCode == null) return null;

        // 调用微信开放平台API获取用户openId
        String openId = exchangeWechatOpenId(wechatCode);
        if (openId == null) return null;

        // 查找绑定的用户
        User user = findByWechatOpenId(openId);
        return user;
    }

    /**
     * 钉钉授权登录验证
     */
    public User verifyByDingtalk(String dingtalkCode) {
        if (dingtalkCode == null) return null;
        String userId = exchangeDingtalkUserId(dingtalkCode);
        if (userId == null) return null;
        return findByDingtalkUserId(userId);
    }

    /**
     * 发送短信验证码
     * 
     * @param phone 手机号
     * @throws RuntimeException 发送频率过高时抛出异常
     */
    public void sendSmsVerificationCode(String phone) {
        // 检查发送频率（60秒内不可重复发送）
        String intervalKey = SMS_CODE_PREFIX + "interval:" + phone;
        if (Boolean.TRUE.equals(redisTemplate.hasKey(intervalKey))) {
            throw new RuntimeException("验证码发送过于频繁，请60秒后重试");
        }

        // 生成6位随机验证码
        String code = String.format("%06d", new Random().nextInt(1000000));

        // 存入Redis（5分钟有效期）
        String codeKey = SMS_CODE_PREFIX + phone;
        redisTemplate.opsForValue().set(codeKey, code, SMS_CODE_EXPIRE, TimeUnit.SECONDS);

        // 设置发送间隔标记（60秒）
        redisTemplate.opsForValue().set(intervalKey, "1", SMS_CODE_INTERVAL, TimeUnit.SECONDS);

        // 调用短信服务发送验证码
        sendSms(phone, code);
    }

    /**
     * 查询用户信息
     */
    public User findById(String userId) {
        // 先查Redis缓存
        // User cachedUser = getCachedUser(userId);
        // if (cachedUser != null) return cachedUser;
        // 查数据库
        // User user = userRepository.findById(userId).orElse(null);
        // if (user != null) cacheUser(user);
        return null;
    }

    /**
     * 根据手机号查询用户
     * 手机号在数据库中AES-256加密存储，查询时需加密后匹配
     */
    public User findByPhone(String phone) {
        String encryptedPhone = encryptField(phone);
        // return userRepository.findByEncryptedPhone(encryptedPhone);
        return null;
    }

    /**
     * 更新用户登录信息
     */
    public void updateLoginInfo(String userId, LocalDateTime loginTime, String loginIp) {
        // userRepository.updateLoginInfo(userId, loginTime, loginIp);
    }

    /**
     * 验证密码
     */
    public boolean verifyPassword(String userId, String password) {
        User user = findById(userId);
        if (user == null) return false;
        return passwordEncoder.matches(password, user.getPasswordHash());
    }

    /**
     * 更新密码
     * 密码使用BCrypt加密后存储，强度因子10
     */
    @Transactional
    public void updatePassword(String userId, String newPassword) {
        // 密码强度校验（最少8位，包含大小写字母和数字）
        if (!isStrongPassword(newPassword)) {
            throw new RuntimeException("密码强度不足，需包含大小写字母和数字，不少于8位");
        }

        String passwordHash = passwordEncoder.encode(newPassword);
        // userRepository.updatePassword(userId, passwordHash);
    }

    /**
     * 将Token加入黑名单（使其立即失效）
     * 黑名单存储在Redis中，有效期与Token过期时间一致
     */
    public void invalidateToken(String token) {
        String key = TOKEN_BLACKLIST_PREFIX + token;
        redisTemplate.opsForValue().set(key, "1", 7200, TimeUnit.SECONDS);
    }

    /**
     * 使用户所有Token失效（强制重新登录）
     */
    public void invalidateAllTokens(String userId) {
        // 更新用户tokenVersion字段，旧版本Token将在校验时失效
        // userRepository.incrementTokenVersion(userId);
    }

    /**
     * 检查Token是否在黑名单中
     */
    public boolean isTokenBlacklisted(String token) {
        String key = TOKEN_BLACKLIST_PREFIX + token;
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

    /**
     * 创建用户
     * 管理员创建教师/学生/家长账户
     */
    @Transactional
    public User createUser(CreateUserRequest request) {
        // 检查手机号唯一性
        if (request.getPhone() != null && findByPhone(request.getPhone()) != null) {
            throw new RuntimeException("手机号已被注册");
        }

        User user = new User();
        user.setId(UUID.randomUUID().toString().replace("-", ""));
        user.setName(request.getName());
        user.setPhone(request.getPhone());
        user.setRole(request.getRole());
        user.setSchoolId(request.getSchoolId());
        user.setSchoolName(request.getSchoolName());
        user.setStatus(1);
        user.setCreateTime(LocalDateTime.now());

        // 加密手机号存储
        if (request.getPhone() != null) {
            user.setEncryptedPhone(encryptField(request.getPhone()));
        }

        // 设置初始密码
        if (request.getPassword() != null) {
            user.setPasswordHash(passwordEncoder.encode(request.getPassword()));
        }

        // userRepository.save(user);
        return user;
    }

    /**
     * 查询学校下的用户列表
     * 按角色过滤（教师/学生/家长）
     */
    public List<User> findBySchoolAndRole(String schoolId, String role) {
        // return userRepository.findBySchoolIdAndRole(schoolId, role);
        return new ArrayList<>();
    }

    // ==================== 内部方法 ====================

    /** 自动注册用户（首次短信登录） */
    private User autoRegister(String phone) {
        User user = new User();
        user.setId(UUID.randomUUID().toString().replace("-", ""));
        user.setPhone(phone);
        user.setEncryptedPhone(encryptField(phone));
        user.setRole("parent"); // 默认家长角色
        user.setStatus(1);
        user.setCreateTime(LocalDateTime.now());
        return user;
    }

    /** 登录失败计数（防暴力破解） */
    private void incrementLoginFailCount(String userId) {
        String key = "writech:login:fail:" + userId;
        Long count = redisTemplate.opsForValue().increment(key);
        if (count != null && count == 1) {
            redisTemplate.expire(key, 1800, TimeUnit.SECONDS); // 30分钟窗口
        }
        if (count != null && count >= 5) {
            // 锁定账户30分钟
            String lockKey = "writech:login:lock:" + userId;
            redisTemplate.opsForValue().set(lockKey, "1", 1800, TimeUnit.SECONDS);
        }
    }

    /** AES-256加密字段（手机号、身份信息等敏感数据） */
    private String encryptField(String plainText) {
        // 使用AES-256-CBC模式加密
        // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        // 实际实现使用配置的密钥
        return Base64.getEncoder().encodeToString(plainText.getBytes());
    }

    /** AES-256解密字段 */
    private String decryptField(String cipherText) {
        return new String(Base64.getDecoder().decode(cipherText));
    }

    /** 密码强度校验 */
    private boolean isStrongPassword(String password) {
        if (password == null || password.length() < 8) return false;
        boolean hasUpper = false, hasLower = false, hasDigit = false;
        for (char c : password.toCharArray()) {
            if (Character.isUpperCase(c)) hasUpper = true;
            if (Character.isLowerCase(c)) hasLower = true;
            if (Character.isDigit(c)) hasDigit = true;
        }
        return hasUpper && hasLower && hasDigit;
    }

    /** 微信OpenId获取（模拟） */
    private String exchangeWechatOpenId(String code) {
        // 调用 https://api.weixin.qq.com/sns/oauth2/access_token
        return null;
    }

    /** 钉钉UserId获取（模拟） */
    private String exchangeDingtalkUserId(String code) {
        return null;
    }

    private User findByWechatOpenId(String openId) { return null; }
    private User findByDingtalkUserId(String userId) { return null; }
    private void sendSms(String phone, String code) { /* 调用短信服务商API */ }

    // ==================== 请求 DTO ====================

    public static class CreateUserRequest {
        private String name;
        private String phone;
        private String password;
        private String role;
        private String schoolId;
        private String schoolName;

        public String getName() { return name; }
        public void setName(String n) { this.name = n; }
        public String getPhone() { return phone; }
        public void setPhone(String p) { this.phone = p; }
        public String getPassword() { return password; }
        public void setPassword(String p) { this.password = p; }
        public String getRole() { return role; }
        public void setRole(String r) { this.role = r; }
        public String getSchoolId() { return schoolId; }
        public void setSchoolId(String id) { this.schoolId = id; }
        public String getSchoolName() { return schoolName; }
        public void setSchoolName(String n) { this.schoolName = n; }
    }
}
