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

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.meowrain.aioj.backend.auth.oauth2.entity.OAuth2Client;
import cn.meowrain.aioj.backend.auth.oauth2.exception.InvalidClientException;
import cn.meowrain.aioj.backend.auth.oauth2.exception.OAuth2Exception;
import cn.meowrain.aioj.backend.auth.oauth2.mapper.OAuth2ClientMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;

/**
 * OAuth2 客户端服务 负责客户端验证和管理
 *
 * @author meowrain
 * @since 2025-12-14
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OAuth2ClientService {

	private final OAuth2ClientMapper clientMapper;

	private final PasswordEncoder passwordEncoder;

	/**
	 * 根据 client_id 获取客户端
	 * @param clientId 客户端 ID
	 * @return 客户端信息
	 * @throws InvalidClientException 客户端不存在或被禁用
	 */
	public OAuth2Client getClientByClientId(String clientId) {
		if (StrUtil.isBlank(clientId)) {
			throw new InvalidClientException("client_id 不能为空");
		}

		OAuth2Client client = clientMapper
			.selectOne(new LambdaQueryWrapper<OAuth2Client>().eq(OAuth2Client::getClientId, clientId));

		if (client == null) {
			log.warn("客户端不存在: {}", clientId);
			throw new InvalidClientException("客户端不存在");
		}

		if (!client.getIsEnabled()) {
			log.warn("客户端已被禁用: {}", clientId);
			throw new InvalidClientException("客户端已被禁用");
		}

		return client;
	}

	/**
	 * 验证客户端密钥（机密客户端）
	 * @param client 客户端信息
	 * @param clientSecret 客户端密钥
	 * @throws InvalidClientException 验证失败
	 */
	public void validateClientSecret(OAuth2Client client, String clientSecret) {
		// 公共客户端不需要验证密钥
		if ("public".equals(client.getClientType())) {
			if (StrUtil.isNotBlank(clientSecret)) {
				log.warn("公共客户端不应该提供 client_secret: {}", client.getClientId());
			}
			return;
		}

		// 机密客户端必须提供密钥
		if (StrUtil.isBlank(clientSecret)) {
			throw new InvalidClientException("机密客户端必须提供 client_secret");
		}

		// 验证密钥
		if (!passwordEncoder.matches(clientSecret, client.getClientSecret())) {
			log.warn("客户端密钥错误: {}", client.getClientId());
			throw new InvalidClientException("客户端认证失败");
		}
	}

	/**
	 * 验证重定向 URI
	 * @param client 客户端信息
	 * @param redirectUri 重定向 URI
	 * @throws OAuth2Exception 验证失败
	 */
	public void validateRedirectUri(OAuth2Client client, String redirectUri) {
		if (StrUtil.isBlank(redirectUri)) {
			throw new OAuth2Exception("redirect_uri 不能为空");
		}

		// 解析客户端配置的重定向 URI 列表
		List<String> allowedUris = parseJsonArray(client.getRedirectUris());

		if (!allowedUris.contains(redirectUri)) {
			log.warn("无效的 redirect_uri: {} for client: {}", redirectUri, client.getClientId());
			throw new OAuth2Exception("无效的 redirect_uri");
		}
	}

	/**
	 * 验证登出重定向 URI
	 * @param client 客户端信息
	 * @param postLogoutRedirectUri 登出重定向 URI
	 * @throws OAuth2Exception 验证失败
	 */
	public void validatePostLogoutRedirectUri(OAuth2Client client, String postLogoutRedirectUri) {
		if (StrUtil.isBlank(postLogoutRedirectUri)) {
			// 登出重定向 URI 可选
			return;
		}

		if (StrUtil.isBlank(client.getPostLogoutRedirectUris())) {
			throw new OAuth2Exception("客户端未配置 post_logout_redirect_uri");
		}

		List<String> allowedUris = parseJsonArray(client.getPostLogoutRedirectUris());

		if (!allowedUris.contains(postLogoutRedirectUri)) {
			log.warn("无效的 post_logout_redirect_uri: {} for client: {}", postLogoutRedirectUri, client.getClientId());
			throw new OAuth2Exception("无效的 post_logout_redirect_uri");
		}
	}

	/**
	 * 验证授权类型
	 * @param client 客户端信息
	 * @param grantType 授权类型
	 * @throws OAuth2Exception 验证失败
	 */
	public void validateGrantType(OAuth2Client client, String grantType) {
		if (StrUtil.isBlank(grantType)) {
			throw new OAuth2Exception("grant_type 不能为空");
		}

		List<String> allowedGrantTypes = Arrays.asList(client.getAllowedGrantTypes().split(","));

		if (!allowedGrantTypes.contains(grantType)) {
			log.warn("客户端 {} 不支持授权类型: {}", client.getClientId(), grantType);
			throw new OAuth2Exception("不支持的 grant_type");
		}
	}

	/**
	 * 验证作用域
	 * @param client 客户端信息
	 * @param requestedScope 请求的作用域（空格分隔）
	 * @return 验证后的作用域（如果为空则返回默认作用域）
	 * @throws OAuth2Exception 验证失败
	 */
	public String validateScope(OAuth2Client client, String requestedScope) {
		// 如果未指定作用域，返回默认作用域
		if (StrUtil.isBlank(requestedScope)) {
			return client.getAllowedScopes().replace(",", " ");
		}

		// 解析允许的作用域
		List<String> allowedScopes = Arrays.asList(client.getAllowedScopes().split(","));

		// 解析请求的作用域
		String[] requestedScopes = requestedScope.split(" ");

		// 验证每个请求的作用域
		for (String scope : requestedScopes) {
			if (!allowedScopes.contains(scope.trim())) {
				log.warn("客户端 {} 不支持作用域: {}", client.getClientId(), scope);
				throw new OAuth2Exception("不支持的 scope: " + scope);
			}
		}

		return requestedScope;
	}

	/**
	 * 解析 JSON 数组字符串
	 * @param jsonArray JSON 数组字符串
	 * @return 列表
	 */
	private List<String> parseJsonArray(String jsonArray) {
		try {
			return JSONUtil.toList(jsonArray, String.class);
		}
		catch (Exception e) {
			log.error("解析 JSON 数组失败: {}", jsonArray, e);
			return List.of();
		}
	}

}
