/**
 * 自然写互动课堂教学管理云平台软件 V1.0
 * 
 * 用户认证控制器
 * 负责用户登录、登出、Token刷新等认证相关接口
 * 采用 JWT Token + Refresh Token 双令牌机制
 */
package com.writech.cloud.controller;

import com.writech.cloud.WritechCloudApplication.ApiResponse;
import com.writech.cloud.WritechCloudApplication.BusinessException;
import com.writech.cloud.model.User;
import com.writech.cloud.service.UserService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.security.Keys;

import javax.crypto.SecretKey;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.time.LocalDateTime;

/**
 * 认证控制器 - /api/v1/auth
 * 
 * 实现教师/学生/管理员/家长多角色用户的统一认证
 * 支持手机号+密码、手机号+验证码、微信/钉钉第三方登录
 */
@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {

    @Autowired
    private UserService userService;

    /** JWT密钥 */
    @Value("${writech.jwt.secret:writech-cloud-platform-jwt-secret-key-2026}")
    private String jwtSecret;

    /** Access Token 有效期（秒），默认2小时 */
    @Value("${writech.jwt.access-token-expire:7200}")
    private long accessTokenExpire;

    /** Refresh Token 有效期（秒），默认7天 */
    @Value("${writech.jwt.refresh-token-expire:604800}")
    private long refreshTokenExpire;

    /**
     * 用户登录接口
     * POST /api/v1/auth/login
     * 
     * 验证用户身份，签发 JWT Access Token 和 Refresh Token
     * Access Token 有效期2小时，Refresh Token 有效期7天
     * 
     * @param request 登录请求（包含手机号、密码/验证码、登录方式）
     * @return 包含双令牌和用户基本信息的响应
     */
    @PostMapping("/login")
    public ApiResponse<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
        // 校验登录参数
        if (request.getLoginType() == null) {
            throw new BusinessException(400, "登录方式不能为空");
        }

        User user = null;

        // 根据不同登录方式验证身份
        switch (request.getLoginType()) {
            case "password":
                // 手机号 + 密码登录
                user = userService.verifyByPassword(request.getPhone(), request.getPassword());
                break;
            case "sms":
                // 手机号 + 短信验证码登录
                user = userService.verifyBySmsCode(request.getPhone(), request.getSmsCode());
                break;
            case "wechat":
                // 微信授权登录
                user = userService.verifyByWechat(request.getWechatCode());
                break;
            case "dingtalk":
                // 钉钉授权登录
                user = userService.verifyByDingtalk(request.getDingtalkCode());
                break;
            default:
                throw new BusinessException(400, "不支持的登录方式: " + request.getLoginType());
        }

        if (user == null) {
            throw new BusinessException(401, "登录失败，用户名或密码错误");
        }

        // 检查用户状态
        if (user.getStatus() != 1) {
            throw new BusinessException(403, "账户已被禁用，请联系管理员");
        }

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

        // 更新用户最后登录时间和登录IP
        userService.updateLoginInfo(user.getId(), LocalDateTime.now(), request.getClientIp());

        // 构建登录响应
        LoginResponse response = new LoginResponse();
        response.setAccessToken(accessToken);
        response.setRefreshToken(refreshToken);
        response.setExpiresIn(accessTokenExpire);
        response.setUserId(user.getId());
        response.setUserName(user.getName());
        response.setRole(user.getRole());
        response.setSchoolId(user.getSchoolId());
        response.setSchoolName(user.getSchoolName());

        return ApiResponse.success(response);
    }

    /**
     * Token 刷新接口
     * POST /api/v1/auth/refresh
     * 
     * 使用 Refresh Token 换取新的 Access Token
     * 避免用户频繁重新登录，提升使用体验
     * 
     * @param request 刷新请求（包含 Refresh Token）
     * @return 新的 Access Token
     */
    @PostMapping("/refresh")
    public ApiResponse<TokenRefreshResponse> refreshToken(@Valid @RequestBody TokenRefreshRequest request) {
        try {
            // 解析并验证 Refresh Token
            Claims claims = parseToken(request.getRefreshToken());
            String userId = claims.getSubject();
            String tokenType = claims.get("type", String.class);

            // 确保是 Refresh Token 类型
            if (!"refresh".equals(tokenType)) {
                throw new BusinessException(401, "无效的刷新令牌");
            }

            // 查询用户信息（确保用户仍然有效）
            User user = userService.findById(userId);
            if (user == null || user.getStatus() != 1) {
                throw new BusinessException(401, "用户不存在或已被禁用");
            }

            // 生成新的 Access Token
            String newAccessToken = generateAccessToken(user);

            TokenRefreshResponse response = new TokenRefreshResponse();
            response.setAccessToken(newAccessToken);
            response.setExpiresIn(accessTokenExpire);

            return ApiResponse.success(response);
        } catch (Exception e) {
            throw new BusinessException(401, "令牌刷新失败: " + e.getMessage());
        }
    }

    /**
     * 用户登出接口
     * POST /api/v1/auth/logout
     * 
     * 将当前 Token 加入黑名单，使其立即失效
     * 同时清除 Redis 中的会话缓存
     */
    @PostMapping("/logout")
    public ApiResponse<Void> logout(@RequestHeader("Authorization") String authorization) {
        String token = extractToken(authorization);
        if (token != null) {
            // 将Token加入Redis黑名单，使其立即失效
            userService.invalidateToken(token);
        }
        return ApiResponse.success();
    }

    /**
     * 发送短信验证码
     * POST /api/v1/auth/sms-code
     * 
     * 向指定手机号发送登录验证码，验证码5分钟内有效
     * 同一手机号60秒内只能发送一次
     */
    @PostMapping("/sms-code")
    public ApiResponse<Void> sendSmsCode(@RequestBody SmsCodeRequest request) {
        if (request.getPhone() == null || request.getPhone().length() != 11) {
            throw new BusinessException(400, "请输入正确的手机号");
        }
        userService.sendSmsVerificationCode(request.getPhone());
        return ApiResponse.success();
    }

    /**
     * 获取当前登录用户信息
     * GET /api/v1/auth/profile
     * 
     * 根据 Token 中的用户ID查询完整的用户信息
     * 包括角色、学校、班级等关联信息
     */
    @GetMapping("/profile")
    public ApiResponse<UserProfileResponse> getProfile(@RequestHeader("Authorization") String authorization) {
        String token = extractToken(authorization);
        Claims claims = parseToken(token);
        String userId = claims.getSubject();

        User user = userService.findById(userId);
        if (user == null) {
            throw new BusinessException(404, "用户不存在");
        }

        UserProfileResponse profile = new UserProfileResponse();
        profile.setUserId(user.getId());
        profile.setName(user.getName());
        profile.setPhone(maskPhone(user.getPhone()));
        profile.setRole(user.getRole());
        profile.setSchoolId(user.getSchoolId());
        profile.setSchoolName(user.getSchoolName());
        profile.setAvatar(user.getAvatar());
        profile.setLastLoginTime(user.getLastLoginTime());

        return ApiResponse.success(profile);
    }

    /**
     * 修改密码
     * PUT /api/v1/auth/password
     */
    @PutMapping("/password")
    public ApiResponse<Void> changePassword(@RequestHeader("Authorization") String authorization,
                                             @Valid @RequestBody ChangePasswordRequest request) {
        String token = extractToken(authorization);
        Claims claims = parseToken(token);
        String userId = claims.getSubject();

        // 验证旧密码
        boolean verified = userService.verifyPassword(userId, request.getOldPassword());
        if (!verified) {
            throw new BusinessException(400, "原密码错误");
        }

        // 更新密码
        userService.updatePassword(userId, request.getNewPassword());
        // 使所有现有Token失效，强制重新登录
        userService.invalidateAllTokens(userId);

        return ApiResponse.success();
    }

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

    /**
     * 生成 Access Token
     * 有效期2小时，包含用户ID、角色、学校信息
     */
    private String generateAccessToken(User user) {
        SecretKey key = Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
        Date now = new Date();
        Date expiry = new Date(now.getTime() + accessTokenExpire * 1000);

        return Jwts.builder()
                .setSubject(user.getId())
                .claim("role", user.getRole())
                .claim("schoolId", user.getSchoolId())
                .claim("type", "access")
                .setIssuedAt(now)
                .setExpiration(expiry)
                .signWith(key, SignatureAlgorithm.HS256)
                .compact();
    }

    /**
     * 生成 Refresh Token
     * 有效期7天，仅包含用户ID和令牌类型
     */
    private String generateRefreshToken(User user) {
        SecretKey key = Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
        Date now = new Date();
        Date expiry = new Date(now.getTime() + refreshTokenExpire * 1000);

        return Jwts.builder()
                .setSubject(user.getId())
                .claim("type", "refresh")
                .setIssuedAt(now)
                .setExpiration(expiry)
                .signWith(key, SignatureAlgorithm.HS256)
                .compact();
    }

    /** 解析 JWT Token */
    private Claims parseToken(String token) {
        SecretKey key = Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
        return Jwts.parserBuilder().setSigningKey(key).build()
                .parseClaimsJws(token).getBody();
    }

    /** 从 Authorization 头中提取 Token */
    private String extractToken(String authorization) {
        if (authorization != null && authorization.startsWith("Bearer ")) {
            return authorization.substring(7);
        }
        return null;
    }

    /** 手机号脱敏处理（中间4位替换为****） */
    private String maskPhone(String phone) {
        if (phone == null || phone.length() != 11) return phone;
        return phone.substring(0, 3) + "****" + phone.substring(7);
    }

    // ==================== 请求/响应 DTO ====================

    /** 登录请求 */
    public static class LoginRequest {
        @NotBlank(message = "登录方式不能为空")
        private String loginType;     // password/sms/wechat/dingtalk
        private String phone;
        private String password;
        private String smsCode;
        private String wechatCode;
        private String dingtalkCode;
        private String clientIp;

        public String getLoginType() { return loginType; }
        public void setLoginType(String loginType) { this.loginType = loginType; }
        public String getPhone() { return phone; }
        public void setPhone(String phone) { this.phone = phone; }
        public String getPassword() { return password; }
        public void setPassword(String password) { this.password = password; }
        public String getSmsCode() { return smsCode; }
        public void setSmsCode(String smsCode) { this.smsCode = smsCode; }
        public String getWechatCode() { return wechatCode; }
        public void setWechatCode(String wechatCode) { this.wechatCode = wechatCode; }
        public String getDingtalkCode() { return dingtalkCode; }
        public void setDingtalkCode(String dingtalkCode) { this.dingtalkCode = dingtalkCode; }
        public String getClientIp() { return clientIp; }
        public void setClientIp(String clientIp) { this.clientIp = clientIp; }
    }

    /** 登录响应 */
    public static class LoginResponse {
        private String accessToken;
        private String refreshToken;
        private long expiresIn;
        private String userId;
        private String userName;
        private String role;
        private String schoolId;
        private String schoolName;

        public String getAccessToken() { return accessToken; }
        public void setAccessToken(String t) { this.accessToken = t; }
        public String getRefreshToken() { return refreshToken; }
        public void setRefreshToken(String t) { this.refreshToken = t; }
        public long getExpiresIn() { return expiresIn; }
        public void setExpiresIn(long e) { this.expiresIn = e; }
        public String getUserId() { return userId; }
        public void setUserId(String id) { this.userId = id; }
        public String getUserName() { return userName; }
        public void setUserName(String n) { this.userName = n; }
        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; }
    }

    /** Token刷新请求 */
    public static class TokenRefreshRequest {
        @NotBlank(message = "刷新令牌不能为空")
        private String refreshToken;
        public String getRefreshToken() { return refreshToken; }
        public void setRefreshToken(String t) { this.refreshToken = t; }
    }

    /** Token刷新响应 */
    public static class TokenRefreshResponse {
        private String accessToken;
        private long expiresIn;
        public String getAccessToken() { return accessToken; }
        public void setAccessToken(String t) { this.accessToken = t; }
        public long getExpiresIn() { return expiresIn; }
        public void setExpiresIn(long e) { this.expiresIn = e; }
    }

    /** 短信验证码请求 */
    public static class SmsCodeRequest {
        private String phone;
        public String getPhone() { return phone; }
        public void setPhone(String p) { this.phone = p; }
    }

    /** 用户信息响应 */
    public static class UserProfileResponse {
        private String userId;
        private String name;
        private String phone;
        private String role;
        private String schoolId;
        private String schoolName;
        private String avatar;
        private LocalDateTime lastLoginTime;

        public String getUserId() { return userId; }
        public void setUserId(String id) { this.userId = id; }
        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 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; }
        public String getAvatar() { return avatar; }
        public void setAvatar(String a) { this.avatar = a; }
        public LocalDateTime getLastLoginTime() { return lastLoginTime; }
        public void setLastLoginTime(LocalDateTime t) { this.lastLoginTime = t; }
    }

    /** 修改密码请求 */
    public static class ChangePasswordRequest {
        @NotBlank(message = "原密码不能为空")
        private String oldPassword;
        @NotBlank(message = "新密码不能为空")
        private String newPassword;
        public String getOldPassword() { return oldPassword; }
        public void setOldPassword(String p) { this.oldPassword = p; }
        public String getNewPassword() { return newPassword; }
        public void setNewPassword(String p) { this.newPassword = p; }
    }
}
