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

import cn.hutool.core.util.StrUtil;
import cn.meowrain.aioj.backend.auth.oauth2.dto.OAuth2TokenRequest;
import cn.meowrain.aioj.backend.auth.oauth2.dto.OAuth2TokenResponse;
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.*;
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.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * OAuth2 Token 端点 处理 Token 请求（授权码换 Token、刷新 Token）
 *
 * @author meowrain
 * @since 2025-12-14
 */
@Slf4j
@RestController
@RequestMapping("/oauth2/token")
@RequiredArgsConstructor
@Tag(name = "OAuth2 Token 端点", description = "OAuth2 Token 相关接口")
public class OAuth2TokenController {

	private final OAuth2ClientService clientService;

	private final OAuth2AuthorizationService authorizationService;

	private final OAuth2TokenService tokenService;

	private final PKCEService pkceService;

	/**
	 * Token 端点
	 * @param request Token 请求
	 * @return Token 响应
	 */
	@PostMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
	@Operation(summary = "获取 Token", description = "使用授权码或刷新令牌获取访问令牌")
	public OAuth2TokenResponse token(OAuth2TokenRequest request) {
		log.info("收到 Token 请求: grantType={}, clientId={}", request.getGrantType(), request.getClientId());

		// 1. 验证 grant_type
		if (StrUtil.isBlank(request.getGrantType())) {
			throw new OAuth2Exception("grant_type 不能为空");
		}

		// 2. 验证客户端
		OAuth2Client client = clientService.getClientByClientId(request.getClientId());
		clientService.validateClientSecret(client, request.getClientSecret());
		clientService.validateGrantType(client, request.getGrantType());

		// 3. 根据授权类型处理
		return switch (request.getGrantType()) {
			case "authorization_code" -> handleAuthorizationCode(client, request);
			case "refresh_token" -> handleRefreshToken(client, request);
			default -> throw new OAuth2Exception("不支持的 grant_type: " + request.getGrantType());
		};
	}

	/**
	 * 处理授权码流程
	 * @param client 客户端
	 * @param request Token 请求
	 * @return Token 响应
	 */
	private OAuth2TokenResponse handleAuthorizationCode(OAuth2Client client, OAuth2TokenRequest request) {
		// 1. 验证必需参数
		if (StrUtil.isBlank(request.getCode())) {
			throw new OAuth2Exception("code 不能为空");
		}
		if (StrUtil.isBlank(request.getRedirectUri())) {
			throw new OAuth2Exception("redirect_uri 不能为空");
		}

		// 2. 验证并消费授权码
		Map<String, Object> codeData = authorizationService.validateAndConsumeCode(request.getCode());

		// 3. 验证授权码绑定
		authorizationService.validateCodeBinding(codeData, request.getClientId(), request.getRedirectUri());

		// 4. 验证 PKCE
		String codeChallenge = tokenService.extractFromCodeData(codeData, "codeChallenge");
		String codeChallengeMethod = tokenService.extractFromCodeData(codeData, "codeChallengeMethod");

		if (StrUtil.isNotBlank(codeChallenge)) {
			if (StrUtil.isBlank(request.getCodeVerifier())) {
				throw new OAuth2Exception("使用了 PKCE，必须提供 code_verifier");
			}
			pkceService.validatePKCE(request.getCodeVerifier(), codeChallenge, codeChallengeMethod);
		}
		else if (client.getRequirePkce()) {
			throw new OAuth2Exception("该客户端必须使用 PKCE");
		}

		// 5. 提取授权码数据
		Long userId = tokenService.extractUserIdFromCodeData(codeData);
		String scope = tokenService.extractFromCodeData(codeData, "scope");
		String nonce = tokenService.extractFromCodeData(codeData, "nonce");

		// 6. 生成 Token
		return tokenService.generateTokenResponse(client, userId, scope, nonce);
	}

	/**
	 * 处理刷新 Token 流程
	 * @param client 客户端
	 * @param request Token 请求
	 * @return Token 响应
	 */
	private OAuth2TokenResponse handleRefreshToken(OAuth2Client client, OAuth2TokenRequest request) {
		// 1. 验证必需参数
		if (StrUtil.isBlank(request.getRefreshToken())) {
			throw new OAuth2Exception("refresh_token 不能为空");
		}

		// 2. 验证 scope（不能超出原范围）
		String scope = request.getScope();
		if (StrUtil.isNotBlank(scope)) {
			scope = clientService.validateScope(client, scope);
		}

		// 3. 刷新 Token
		return tokenService.refreshToken(client, request.getRefreshToken(), scope);
	}

}
