package com.lutao.cloud.auth.service;
/**
 * Copyright (c) 2022 cloud Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.lutao.cloud.admin.api.entity.SysOauthClientDetails;
import com.lutao.cloud.admin.api.feign.RemoteClientDetailsService;
import com.lutao.cloud.common.core.constant.CacheConstants;
import com.lutao.cloud.common.core.constant.SecurityConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Map;

/**

 * @date 2020/03/25
 * <p>
 * 扩展 JdbcClientDetailsService 支持多租户
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CloudClientDetailsServiceImpl implements ClientDetailsService{

	private final RemoteClientDetailsService clientDetailsService;

	/**
	 * 重写原生方法支持redis缓存
	 * @param clientId
	 * @return ClientDetails
	 * @throws InvalidClientException
	 * 不是空的时候缓存
	 */
	@Override
	@Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless = "#result == null")
	public ClientDetails loadClientByClientId(String clientId){
		SysOauthClientDetails clientDetails = clientDetailsService
				.getClientDetailsById(clientId, SecurityConstants.FROM_IN).getData();

		if (clientDetails == null){
			return null;
		}
		// 适配成oauth2内置类型
		return clientDetailsWrapper(clientDetails);
	}

	/**
	 * 客户端类型转化 参考
	 * 1.检查clientId
	 * 2.检查GrantType
	 * 3.检查Authorities
	 * 4.检查ResourceIds
	 * 5.检查RedirectUri
	 * 6.检查Scope
	 * 7.检查Autoapprove
	 * 8.检查token有效性
	 * 9.检查刷新token的有效性
	 *{@link org.springframework.security.oauth2.provider.client.JdbcClientDetailsService}
	 * @param origin 数据库保存参数
	 * @return target oauth 类型客户端参数
	 *
	 */
	private ClientDetails clientDetailsWrapper(SysOauthClientDetails origin){
		BaseClientDetails target = new BaseClientDetails();
		// 必选项
		target.setClientId(origin.getClientId());
		target.setClientSecret(String.format("{noop}%s", origin.getClientSecret()));
		// 检查GrantType
		if (ArrayUtil.isNotEmpty(origin.getAuthorizedGrantTypes())){
			target.setAuthorizedGrantTypes(CollUtil.newArrayList(origin.getAuthorizedGrantTypes()));
		}

		// 检查Authorities
		if (StrUtil.isNotBlank(origin.getAuthorities())){
			target.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(origin.getAuthorities()));
		}

		// 检查ResourceIds
		if (StrUtil.isNotBlank(origin.getResourceIds())){
			target.setResourceIds(StringUtils.commaDelimitedListToSet(origin.getResourceIds()));
		}

		// 检查RedirectUri
		if (StrUtil.isNotBlank(origin.getWebServerRedirectUri())){
			target.setRegisteredRedirectUri(StringUtils.commaDelimitedListToSet(origin.getWebServerRedirectUri()));
		}

		// 检查Scope
		if (StrUtil.isNotBlank(origin.getScope())){
			target.setScope(StringUtils.commaDelimitedListToSet(origin.getScope()));
		}

		// 检查Autoapprove
		if (StrUtil.isNotBlank(origin.getAutoapprove())){
			target.setAutoApproveScopes(StringUtils.commaDelimitedListToSet(origin.getAutoapprove()));
		}

		// 检查token有效性
		if (origin.getAccessTokenValidity() != null){
			target.setAccessTokenValiditySeconds(origin.getAccessTokenValidity());
		}

		// 检查刷新token有效性
		if (origin.getRefreshTokenValidity() != null){
			target.setRefreshTokenValiditySeconds(origin.getRefreshTokenValidity());
		}

		String json = origin.getAdditionalInformation();
		if (StrUtil.isNotBlank(json)){
			try{
				@SuppressWarnings("unchecked")
				Map<String, Object> additionalInformation = JSONUtil.toBean(json, Map.class);
				target.setAdditionalInformation(additionalInformation);
			}
			catch (Exception e){
				log.warn("Could not decode JSON for additional information: " + json, e);
			}
		}

		return target;
	}

}
