package cn.meowrain.aioj.backend.auth.oauth2.controller;

import cn.hutool.core.util.StrUtil;
import cn.meowrain.aioj.backend.auth.oauth2.dto.OAuth2AuthorizeRequest;
import cn.meowrain.aioj.backend.auth.oauth2.entity.OAuth2Client;
import cn.meowrain.aioj.backend.auth.oauth2.exception.OAuth2Exception;
import cn.meowrain.aioj.backend.auth.oauth2.service.OAuth2AuthorizationService;
import cn.meowrain.aioj.backend.auth.oauth2.service.OAuth2ClientService;
import cn.meowrain.aioj.backend.auth.oauth2.service.PKCEService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.view.RedirectView;

/**
 * OAuth2 授权端点 处理授权请求并生成授权码
 *
 * @author meowrain
 * @since 2025-12-14
 */
@Slf4j
@RestController
@RequestMapping("/oauth2/authorize")
@RequiredArgsConstructor
@Tag(name = "OAuth2 授权端点", description = "OAuth2 授权相关接口")
public class OAuth2AuthorizationController {

	private final OAuth2ClientService clientService;

	private final OAuth2AuthorizationService authorizationService;

	private final PKCEService pkceService;

	/**
	 * 授权端点（GET）
	 * @param request 授权请求
	 * @return 重定向到客户端
	 */
	@GetMapping
	@Operation(summary = "OAuth2 授权（GET）", description = "发起 OAuth2 授权请求")
	public RedirectView authorize(OAuth2AuthorizeRequest request) {
		return processAuthorization(request);
	}

	/**
	 * 授权端点（POST）
	 * @param request 授权请求
	 * @return 重定向到客户端
	 */
	@PostMapping
	@Operation(summary = "OAuth2 授权（POST）", description = "发起 OAuth2 授权请求")
	public RedirectView authorizePost(OAuth2AuthorizeRequest request) {
		return processAuthorization(request);
	}

	/**
	 * 处理授权请求
	 * @param request 授权请求
	 * @return 重定向视图
	 */
	private RedirectView processAuthorization(OAuth2AuthorizeRequest request) {
		log.info("收到授权请求: clientId={}, redirectUri={}, scope={}", request.getClientId(), request.getRedirectUri(),
				request.getScope());

		try {
			// 1. 验证基本参数
			validateBasicParameters(request);

			// 2. 验证客户端
			OAuth2Client client = clientService.getClientByClientId(request.getClientId());
			clientService.validateRedirectUri(client, request.getRedirectUri());

			// 3. 验证 response_type
			if (!"code".equals(request.getResponseType())) {
				throw new OAuth2Exception("不支持的 response_type，仅支持 code");
			}

			// 4. 验证 PKCE（如果客户端要求）
			pkceService.validatePKCERequest(request.getCodeChallenge(), request.getCodeChallengeMethod(),
					client.getRequirePkce());

			// 5. 验证 scope
			String scope = clientService.validateScope(client, request.getScope());
			request.setScope(scope);

			// 6. 检查用户是否已登录
			Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
			if (authentication == null || !authentication.isAuthenticated()
					|| "anonymousUser".equals(authentication.getPrincipal())) {
				// 用户未登录，重定向到登录页面
				// TODO: 实现登录页面，登录成功后重定向回授权端点
				String loginUrl = buildLoginRedirectUrl(request);
				log.info("用户未登录，重定向到登录页面: {}", loginUrl);
				return new RedirectView(loginUrl);
			}

			// 7. 提取用户 ID
			// 从 SecurityContext 中提取用户ID（根据你的实际实现调整）
			Long userId = extractUserIdFromAuthentication(authentication);
			request.setUserId(userId);

			// 8. 生成授权码
			String code = authorizationService.generateAuthorizationCode(request);

			// 9. 构造重定向 URL
			String redirectUrl = buildRedirectUrl(request.getRedirectUri(), code, request.getState());

			log.info("授权成功，重定向到客户端: {}", redirectUrl);

			return new RedirectView(redirectUrl);
		}
		catch (OAuth2Exception e) {
			// OAuth2 异常：重定向到客户端并携带错误信息
			String errorUrl = buildErrorRedirectUrl(request.getRedirectUri(), e.getError(), e.getErrorDescription(),
					request.getState());
			log.error("授权失败: {}", e.getErrorDescription());
			return new RedirectView(errorUrl);
		}
		catch (Exception e) {
			// 其他异常：返回 500 错误
			log.error("授权过程发生异常", e);
			throw new OAuth2Exception("server_error", "服务器内部错误");
		}
	}

	/**
	 * 验证基本参数
	 * @param request 授权请求
	 */
	private void validateBasicParameters(OAuth2AuthorizeRequest request) {
		if (StrUtil.isBlank(request.getResponseType())) {
			throw new OAuth2Exception("response_type 不能为空");
		}
		if (StrUtil.isBlank(request.getClientId())) {
			throw new OAuth2Exception("client_id 不能为空");
		}
		if (StrUtil.isBlank(request.getRedirectUri())) {
			throw new OAuth2Exception("redirect_uri 不能为空");
		}
	}

	/**
	 * 从 Authentication 中提取用户 ID
	 * @param authentication 认证信息
	 * @return 用户 ID
	 */
	private Long extractUserIdFromAuthentication(Authentication authentication) {
		// 根据你的实际实现调整
		// 示例：从 JWT 的 Claims 中提取
		Object principal = authentication.getPrincipal();
		if (principal instanceof Long) {
			return (Long) principal;
		}

		// 如果是字符串，尝试解析
		try {
			return Long.parseLong(principal.toString());
		}
		catch (NumberFormatException e) {
			log.error("无法从 Authentication 中提取用户 ID: {}", principal);
			throw new OAuth2Exception("无法获取用户信息");
		}
	}

	/**
	 * 构造重定向 URL（成功）
	 * @param redirectUri 重定向 URI
	 * @param code 授权码
	 * @param state 状态参数
	 * @return 重定向 URL
	 */
	private String buildRedirectUrl(String redirectUri, String code, String state) {
		StringBuilder url = new StringBuilder(redirectUri);
		url.append(redirectUri.contains("?") ? "&" : "?");
		url.append("code=").append(code);

		if (StrUtil.isNotBlank(state)) {
			url.append("&state=").append(state);
		}

		return url.toString();
	}

	/**
	 * 构造错误重定向 URL
	 * @param redirectUri 重定向 URI
	 * @param error 错误代码
	 * @param errorDescription 错误描述
	 * @param state 状态参数
	 * @return 错误重定向 URL
	 */
	private String buildErrorRedirectUrl(String redirectUri, String error, String errorDescription, String state) {
		if (StrUtil.isBlank(redirectUri)) {
			// 如果没有 redirect_uri，无法重定向，直接抛出异常
			throw new OAuth2Exception(error, errorDescription);
		}

		StringBuilder url = new StringBuilder(redirectUri);
		url.append(redirectUri.contains("?") ? "&" : "?");
		url.append("error=").append(error);

		if (StrUtil.isNotBlank(errorDescription)) {
			url.append("&error_description=").append(errorDescription);
		}

		if (StrUtil.isNotBlank(state)) {
			url.append("&state=").append(state);
		}

		return url.toString();
	}

	/**
	 * 构造登录重定向 URL
	 * @param request 授权请求
	 * @return 登录 URL
	 */
	private String buildLoginRedirectUrl(OAuth2AuthorizeRequest request) {
		// TODO: 根据实际情况调整登录页面 URL
		// 登录成功后应该重定向回 /oauth2/authorize 并携带所有参数
		StringBuilder loginUrl = new StringBuilder("/login");
		loginUrl.append("?redirect_uri=").append("/oauth2/authorize");
		loginUrl.append("&client_id=").append(request.getClientId());
		// ... 添加其他参数

		return loginUrl.toString();
	}

}
