package com.dora.controller;

import com.dora.common.Result;
import com.dora.dto.AuthDto;
import com.dora.service.JwtTokenManager;
import com.dora.service.UserService;
import com.dora.util.JwtUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

/**
 * 用户端认证控制器
 */
@Slf4j
@RestController
@RequestMapping("/user/auth")
@RequiredArgsConstructor
@Tag(name = "用户认证", description = "用户端认证相关接口")
public class AuthController {

    private final UserService userService;
    private final JwtUtil jwtUtil;
    private final JwtTokenManager jwtTokenManager;

    @PostMapping("/sms-login")
    @Operation(summary = "短信验证码登录", description = "使用手机号和短信验证码进行登录")
    public Result<AuthDto.LoginResponse> smsLogin(
            @Valid @RequestBody AuthDto.SmsLoginRequest request) {
        try {
            AuthDto.LoginResponse response = userService.smsLogin(request.getPhone(), request.getCode());
            return Result.success(response);
        } catch (Exception e) {
            log.error("短信登录失败 - phone: {}", request.getPhone(), e);
            return Result.error(400, e.getMessage());
        }
    }

    @PostMapping("/password-login")
    @Operation(summary = "密码登录", description = "使用手机号和密码进行登录")
    public Result<AuthDto.LoginResponse> passwordLogin(
            @Valid @RequestBody AuthDto.PasswordLoginRequest request) {
        try {
            AuthDto.LoginResponse response = userService.passwordLogin(request.getPhone(), request.getPassword());
            return Result.success(response);
        } catch (Exception e) {
            log.error("密码登录失败 - phone: {}", request.getPhone(), e);
            return Result.error(400, e.getMessage());
        }
    }

    @PostMapping("/register")
    @Operation(summary = "用户注册", description = "使用手机号、验证码和可选的密码、邀请码进行注册。密码为空时用户只能使用短信验证码登录。")
    public Result<AuthDto.LoginResponse> register(
            @Valid @RequestBody AuthDto.RegisterRequest request) {
        try {
            AuthDto.LoginResponse response = userService.register(
                request.getPhone(), 
                request.getCode(), 
                request.getPassword(),
                request.getInviteCode()
            );
            return Result.success(response);
        } catch (Exception e) {
            log.error("用户注册失败 - phone: {}", request.getPhone(), e);
            return Result.error(400, e.getMessage());
        }
    }


    @PostMapping("/refresh-token")
    @Operation(summary = "刷新Token", description = "刷新用户的JWT token以延长登录状态。只有当token即将过期时才会生成新token。")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "刷新成功",
                content = @Content(schema = @Schema(implementation = Result.class))),
        @ApiResponse(responseCode = "401", description = "token无效或已过期"),
        @ApiResponse(responseCode = "500", description = "服务器内部错误")
    })
    public ResponseEntity<Result<AuthDto.RefreshTokenResponse>> refreshToken(HttpServletRequest request) {
        try {
            // 1. 获取Authorization header
            String authHeader = request.getHeader("Authorization");
            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(Result.error(401, "未提供认证令牌"));
            }

            // 2. 提取token
            String token = authHeader.substring(7);
            
            // 3. 调用JWT工具类进行token刷新
            JwtUtil.RefreshTokenResult result = jwtUtil.refreshUserToken(token);
            
            // 4. 构建响应
            AuthDto.RefreshTokenResponse response = new AuthDto.RefreshTokenResponse();
            response.setSuccess(result.isSuccess());
            response.setRefreshed(result.isRefreshed());
            response.setMessage(result.getMessage());
            
            if (result.isSuccess()) {
                response.setToken(result.getToken());
                // 格式化过期时间
                if (result.getExpiration() != null) {
                    response.setTokenExpiresAt(formatTokenExpirationTime(result.getExpiration()));
                }
                
                log.info("Token刷新请求处理完成 - 刷新状态: {}, 消息: {}", 
                    result.isRefreshed() ? "已刷新" : "无需刷新", result.getMessage());
                
                return ResponseEntity.ok(Result.success(response));
            } else {
                log.warn("Token刷新失败: {}", result.getMessage());
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(Result.error(401, result.getMessage()));
            }
            
        } catch (Exception e) {
            log.error("刷新token失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Result.error(500, "刷新token失败"));
        }
    }

    @GetMapping("/me")
    @Operation(summary = "获取当前用户信息", description = "获取当前登录用户的详细信息")
    public ResponseEntity<Result<AuthDto.UserInfoResponse>> getCurrentUser(HttpServletRequest request) {
        try {
            String authHeader = request.getHeader("Authorization");
            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Result.error(401, "未提供认证令牌"));
            }

            String token = authHeader.substring(7);
            if (!jwtUtil.validateToken(token)) {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Result.error(401, "认证令牌无效或已过期"));
            }

            Long userId = jwtUtil.getUserIdFromToken(token);
            AuthDto.UserInfoResponse userInfo = userService.getCurrentUserInfo(userId);
            return ResponseEntity.ok(Result.success(userInfo));
        } catch (Exception e) {
            log.error("获取当前用户信息失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, "获取用户信息失败"));
        }
    }

    @PutMapping("/profile")
    @Operation(summary = "修改用户信息", description = "修改用户昵称和头像等基本信息")
    public ResponseEntity<Result<String>> updateProfile(
            @Valid @RequestBody AuthDto.UpdateProfileRequest request,
            HttpServletRequest httpRequest) {
        try {
            Long userId = getUserIdFromRequest(httpRequest);
            boolean success = userService.updateProfile(userId, request.getUsername(), request.getAvatarUrl());
            if (success) {
                return ResponseEntity.ok(Result.success("用户信息更新成功"));
            } else {
                return ResponseEntity.badRequest().body(Result.error(400, "用户信息更新失败"));
            }
        } catch (RuntimeException e) {
            // JWT相关异常返回401
            if (e.getMessage().contains("认证令牌") || e.getMessage().contains("未提供认证令牌")) {
                log.warn("JWT认证失败: {}", e.getMessage());
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Result.error(401, e.getMessage()));
            }
            log.error("更新用户信息失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, e.getMessage()));
        } catch (Exception e) {
            log.error("更新用户信息失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, "服务器内部错误"));
        }
    }

    @PutMapping("/username")
    @Operation(summary = "修改用户名", description = "修改用户登录用户名")
    public ResponseEntity<Result<String>> updateUsername(
            @Valid @RequestBody AuthDto.UpdateUsernameRequest request,
            HttpServletRequest httpRequest) {
        try {
            Long userId = getUserIdFromRequest(httpRequest);
            boolean success = userService.updateUsername(userId, request.getNewUsername());
            if (success) {
                return ResponseEntity.ok(Result.success("用户名修改成功"));
            } else {
                return ResponseEntity.badRequest().body(Result.error(400, "用户名修改失败"));
            }
        } catch (RuntimeException e) {
            // JWT相关异常返回401
            if (e.getMessage().contains("认证令牌") || e.getMessage().contains("未提供认证令牌")) {
                log.warn("JWT认证失败: {}", e.getMessage());
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Result.error(401, e.getMessage()));
            }
            log.error("修改用户名失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, e.getMessage()));
        } catch (Exception e) {
            log.error("修改用户名失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, "服务器内部错误"));
        }
    }

    @PostMapping("/reset-password")
    @Operation(summary = "忘记密码重置", description = "通过手机号和验证码重置密码")
    public Result<String> resetPassword(
            @Valid @RequestBody AuthDto.ResetPasswordRequest request) {
        try {
            boolean success = userService.resetPassword(request.getPhone(), request.getCode(), request.getNewPassword());
            if (success) {
                return Result.success("密码重置成功");
            } else {
                return Result.error(400, "密码重置失败");
            }
        } catch (Exception e) {
            log.error("重置密码失败 - phone: {}", request.getPhone(), e);
            return Result.error(400, e.getMessage());
        }
    }

    @GetMapping("/username/check")
    @Operation(summary = "检查用户名是否可用", description = "检查用户名是否已被使用")
    public Result<Boolean> checkUsername(
            @Parameter(description = "用户名") @RequestParam String username) {
        try {
            boolean available = userService.isUsernameAvailable(username);
            return Result.success(available);
        } catch (Exception e) {
            log.error("检查用户名失败 - username: {}", username, e);
            return Result.error(500, "检查用户名失败");
        }
    }

    @PutMapping("/real-identity")
    @Operation(summary = "修改真实身份信息", description = "修改当前用户的真实用户名和身份证号码")
    public ResponseEntity<Result<String>> updateRealIdentity(
            @Valid @RequestBody AuthDto.UpdateRealIdentityRequest request,
            HttpServletRequest httpRequest) {
        try {
            Long userId = getUserIdFromRequest(httpRequest);
            boolean success = userService.updateRealIdentity(userId, request.getRealUsername(), request.getIdNumber());
            if (success) {
                return ResponseEntity.ok(Result.success("真实身份信息更新成功"));
            } else {
                return ResponseEntity.badRequest().body(Result.error(400, "真实身份信息更新失败"));
            }
        } catch (RuntimeException e) {
            // JWT相关异常返回401
            if (e.getMessage().contains("认证令牌") || e.getMessage().contains("未提供认证令牌")) {
                log.warn("JWT认证失败: {}", e.getMessage());
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Result.error(401, e.getMessage()));
            }
            log.error("更新真实身份信息失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, e.getMessage()));
        } catch (Exception e) {
            log.error("更新真实身份信息失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, "服务器内部错误"));
        }
    }

    @GetMapping("/real-identity")
    @Operation(summary = "获取真实身份信息", description = "获取当前用户的真实身份信息")
    public ResponseEntity<Result<AuthDto.RealIdentityResponse>> getRealIdentity(HttpServletRequest request) {
        try {
            Long userId = getUserIdFromRequest(request);
            AuthDto.RealIdentityResponse response = userService.getRealIdentity(userId);
            return ResponseEntity.ok(Result.success(response, "获取真实身份信息成功"));
        } catch (RuntimeException e) {
            // JWT相关异常返回401
            if (e.getMessage().contains("认证令牌") || e.getMessage().contains("未提供认证令牌")) {
                log.warn("JWT认证失败: {}", e.getMessage());
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Result.error(401, e.getMessage()));
            }
            log.error("获取真实身份信息失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, e.getMessage()));
        } catch (Exception e) {
            log.error("获取真实身份信息失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, "服务器内部错误"));
        }
    }

    @PutMapping("/phone")
    @Operation(summary = "更新手机号", description = "更新当前用户的手机号，需要验证码验证")
    public ResponseEntity<Result<String>> updatePhone(
            @Valid @RequestBody AuthDto.UpdatePhoneRequest request,
            HttpServletRequest httpRequest) {
        try {
            Long userId = getUserIdFromRequest(httpRequest);
            boolean success = userService.updatePhone(userId, request.getNewPhone(), request.getCode());
            if (success) {
                return ResponseEntity.ok(Result.success("手机号更新成功"));
            } else {
                return ResponseEntity.badRequest().body(Result.error(400, "手机号更新失败"));
            }
        } catch (RuntimeException e) {
            // JWT相关异常返回401
            if (e.getMessage().contains("认证令牌") || e.getMessage().contains("未提供认证令牌")) {
                log.warn("JWT认证失败: {}", e.getMessage());
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Result.error(401, e.getMessage()));
            }
            // 业务异常（验证码错误、手机号已被使用等）返回400，并传递具体错误信息
            String errorMsg = e.getMessage();
            if (errorMsg != null && errorMsg.startsWith("更新手机号失败: ")) {
                // 提取Service层包装的具体错误原因
                errorMsg = errorMsg.substring("更新手机号失败: ".length());
            }
            log.error("更新手机号失败: {}", errorMsg, e);
            return ResponseEntity.badRequest().body(Result.error(400, errorMsg));
        } catch (Exception e) {
            log.error("更新手机号失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, "服务器内部错误"));
        }
    }

    @PutMapping("/password")
    @Operation(summary = "修改密码", description = "通过手机号和验证码修改密码")
    public ResponseEntity<Result<String>> updatePassword(
            @Valid @RequestBody AuthDto.UpdatePasswordRequest request) {
        try {
            boolean success = userService.updatePasswordWithCode(request.getPhone(), request.getCode(), request.getNewPassword());
            if (success) {
                return ResponseEntity.ok(Result.success("密码修改成功"));
            } else {
                return ResponseEntity.badRequest().body(Result.error(400, "密码修改失败"));
            }
        } catch (RuntimeException e) {
            log.error("修改密码失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, e.getMessage()));
        } catch (Exception e) {
            log.error("修改密码失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, "服务器内部错误"));
        }
    }

    @PostMapping("/verify-password")
    @Operation(summary = "验证当前密码", description = "验证当前用户的密码是否正确")
    public ResponseEntity<Result<Boolean>> verifyPassword(
            @Valid @RequestBody AuthDto.VerifyPasswordRequest request,
            HttpServletRequest httpRequest) {
        try {
            Long userId = getUserIdFromRequest(httpRequest);
            boolean isValid = userService.verifyCurrentPassword(userId, request.getPassword());
            if (isValid) {
                return ResponseEntity.ok(Result.success(true, "密码验证成功"));
            } else {
                return ResponseEntity.ok(Result.success(false, "密码验证失败"));
            }
        } catch (RuntimeException e) {
            // JWT相关异常返回401
            if (e.getMessage().contains("认证令牌") || e.getMessage().contains("未提供认证令牌")) {
                log.warn("JWT认证失败: {}", e.getMessage());
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Result.error(401, e.getMessage()));
            }
            log.error("验证密码失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, e.getMessage()));
        } catch (Exception e) {
            log.error("验证密码失败", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Result.error(500, "服务器内部错误"));
        }
    }

    /**
     * 从请求中获取用户ID
     */
    private Long getUserIdFromRequest(HttpServletRequest request) {
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            throw new RuntimeException("未提供认证令牌");
        }

        String token = authHeader.substring(7);
        if (!jwtUtil.validateToken(token)) {
            throw new RuntimeException("认证令牌无效或已过期");
        }

        return jwtUtil.getUserIdFromToken(token);
    }

    /**
     * 用户登出
     */
    @PostMapping("/logout")
    @Operation(summary = "用户登出", description = "用户主动登出，清理当前设备的token")
    public Result<String> logout(HttpServletRequest request) {
        try {
            String authHeader = request.getHeader("Authorization");
            if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Bearer ")) {
                return Result.error(400, "未提供认证令牌");
            }

            String token = authHeader.substring(7);
            if (!jwtUtil.validateToken(token)) {
                return Result.error(400, "认证令牌无效或已过期");
            }

            // 获取用户信息
            Long userId = jwtUtil.getUserIdFromToken(token);
            String jwtId = jwtUtil.getJwtIdFromToken(token);

            if (userId != null && jwtId != null) {
                // 从token管理器中移除当前token
                jwtTokenManager.removeUserToken(userId, jwtId);
                log.info("用户登出成功 - userId: {}, jwtId: {}", userId, jwtId);
                return Result.success("登出成功");
            } else {
                return Result.error(400, "token信息不完整");
            }

        } catch (Exception e) {
            log.error("用户登出失败", e);
            return Result.error(500, "登出失败: " + e.getMessage());
        }
    }

    /**
     * 强制登出所有设备
     */
    @PostMapping("/logout-all-devices")
    @Operation(summary = "登出所有设备", description = "用户强制登出所有设备，清理所有token")
    public Result<String> logoutAllDevices(HttpServletRequest request) {
        try {
            Long userId = getUserIdFromRequest(request);
            
            // 强制下线所有设备
            jwtTokenManager.forceLogoutAllUserDevices(userId);
            
            log.info("用户强制下线所有设备 - userId: {}", userId);
            return Result.success("已登出所有设备");
            
        } catch (Exception e) {
            log.error("强制登出所有设备失败", e);
            return Result.error(500, "操作失败: " + e.getMessage());
        }
    }

    /**
     * 获取当前活跃设备数
     */
    @GetMapping("/active-devices")
    @Operation(summary = "获取活跃设备数", description = "获取当前用户的活跃登录设备数量")
    public Result<Long> getActiveDevicesCount(HttpServletRequest request) {
        try {
            Long userId = getUserIdFromRequest(request);
            long count = jwtTokenManager.getUserActiveTokenCount(userId);
            
            log.debug("获取用户活跃设备数 - userId: {}, count: {}", userId, count);
            return Result.success(count);
            
        } catch (Exception e) {
            log.error("获取活跃设备数失败", e);
            return Result.error(500, "操作失败: " + e.getMessage());
        }
    }

    /**
     * 格式化token过期时间
     * @param expirationTime 过期时间
     * @return 格式化后的时间字符串
     */
    private String formatTokenExpirationTime(java.util.Date expirationTime) {
        java.time.Instant instant = expirationTime.toInstant();
        java.time.LocalDateTime localDateTime = instant.atZone(java.time.ZoneId.systemDefault()).toLocalDateTime();
        return localDateTime.format(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME);
    }
} 