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

import cn.hutool.core.util.StrUtil;
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.OAuth2ClientService;
import cn.meowrain.aioj.backend.auth.oauth2.service.OAuth2SessionService;
import cn.meowrain.aioj.backend.framework.security.utils.JwtUtil;
import io.jsonwebtoken.Claims;
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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
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/logout")
@RequiredArgsConstructor
@Tag(name = "OAuth2 登出端点", description = "OAuth2 登出相关接口")
public class OAuth2LogoutController {

	private final OAuth2SessionService sessionService;

	private final OAuth2ClientService clientService;

	private final JwtUtil jwtUtil;

	/**
	 * 登出端点（GET）
	 * @param idTokenHint ID Token Hint（可选）
	 * @param postLogoutRedirectUri 登出后重定向URI（可选）
	 * @param clientId 客户端ID（可选，用于验证redirect_uri）
	 * @param state 状态参数（可选）
	 * @return 重定向视图
	 */
	@GetMapping
	@Operation(summary = "OAuth2 登出（GET）", description = "单点登出，撤销所有会话")
	public RedirectView logout(@RequestParam(required = false) String idTokenHint,
			@RequestParam(name = "post_logout_redirect_uri", required = false) String postLogoutRedirectUri,
			@RequestParam(name = "client_id", required = false) String clientId,
			@RequestParam(required = false) String state) {
		return processLogout(idTokenHint, postLogoutRedirectUri, clientId, state);
	}

	/**
	 * 登出端点（POST）
	 * @param idTokenHint ID Token Hint（可选）
	 * @param postLogoutRedirectUri 登出后重定向URI（可选）
	 * @param clientId 客户端ID（可选）
	 * @param state 状态参数（可选）
	 * @return 重定向视图
	 */
	@PostMapping
	@Operation(summary = "OAuth2 登出（POST）", description = "单点登出，撤销所有会话")
	public RedirectView logoutPost(@RequestParam(required = false) String idTokenHint,
			@RequestParam(name = "post_logout_redirect_uri", required = false) String postLogoutRedirectUri,
			@RequestParam(name = "client_id", required = false) String clientId,
			@RequestParam(required = false) String state) {
		return processLogout(idTokenHint, postLogoutRedirectUri, clientId, state);
	}

	/**
	 * 处理登出请求
	 * @param idTokenHint ID Token Hint
	 * @param postLogoutRedirectUri 登出后重定向URI
	 * @param clientId 客户端ID
	 * @param state 状态参数
	 * @return 重定向视图
	 */
	private RedirectView processLogout(String idTokenHint, String postLogoutRedirectUri, String clientId,
			String state) {
		log.info("收到登出请求: clientId={}, postLogoutRedirectUri={}", clientId, postLogoutRedirectUri);

		try {
			// 1. 提取用户ID
			Long userId = extractUserIdFromToken(idTokenHint);

			if (userId == null) {
				log.warn("无法从 id_token_hint 中提取用户ID");
				throw new OAuth2Exception("invalid_request", "无效的 id_token_hint");
			}

			// 2. 撤销用户的所有会话
			sessionService.revokeAllUserSessions(userId);

			log.info("用户登出成功: userId={}", userId);

			// 3. 验证并重定向
			if (StrUtil.isNotBlank(postLogoutRedirectUri)) {
				// 验证 redirect_uri（如果提供了 client_id）
				if (StrUtil.isNotBlank(clientId)) {
					OAuth2Client client = clientService.getClientByClientId(clientId);
					clientService.validatePostLogoutRedirectUri(client, postLogoutRedirectUri);
				}

				// 构造重定向URL
				String redirectUrl = buildLogoutRedirectUrl(postLogoutRedirectUri, state);
				log.info("重定向到: {}", redirectUrl);
				return new RedirectView(redirectUrl);
			}

			// 4. 如果没有 redirect_uri，返回默认页面
			log.info("登出成功，无重定向URI");
			return new RedirectView("/logout-success"); // TODO: 自定义登出成功页面
		}
		catch (OAuth2Exception e) {
			log.error("登出失败: {}", e.getErrorDescription());
			throw e;
		}
		catch (Exception e) {
			log.error("登出过程发生异常", e);
			throw new OAuth2Exception("server_error", "服务器内部错误");
		}
	}

	/**
	 * 从 Token 中提取用户 ID
	 * @param token Token（ID Token 或 Access Token）
	 * @return 用户 ID
	 */
	private Long extractUserIdFromToken(String token) {
		if (StrUtil.isBlank(token)) {
			return null;
		}

		try {
			// 验证 Token
			if (!jwtUtil.isTokenValid(token)) {
				log.warn("Token 无效或已过期");
				return null;
			}

			// 解析 Token
			Claims claims = jwtUtil.parseClaims(token);

			// 尝试从 sub claim 提取用户ID
			String sub = claims.getSubject();
			if (StrUtil.isNotBlank(sub)) {
				return Long.parseLong(sub);
			}

			// 尝试从 userId claim 提取
			Object userId = claims.get("userId");
			if (userId != null) {
				return Long.parseLong(userId.toString());
			}

			return null;
		}
		catch (Exception e) {
			log.error("解析 Token 失败", e);
			return null;
		}
	}

	/**
	 * 构造登出重定向 URL
	 * @param postLogoutRedirectUri 登出后重定向URI
	 * @param state 状态参数
	 * @return 重定向 URL
	 */
	private String buildLogoutRedirectUrl(String postLogoutRedirectUri, String state) {
		if (StrUtil.isBlank(state)) {
			return postLogoutRedirectUri;
		}

		StringBuilder url = new StringBuilder(postLogoutRedirectUri);
		url.append(postLogoutRedirectUri.contains("?") ? "&" : "?");
		url.append("state=").append(state);

		return url.toString();
	}

}
