package com.css.txw.sso.controller.oauth2;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.css.ggzc.framework.common.pojo.CommonResult;
import com.css.ggzc.framework.common.util.gy.GyUtils;
import com.css.txw.sso.constants.SsoApiConstants;
import com.css.txw.sso.constants.SsoConstants;
import com.css.txw.sso.convert.OAuth2OpenConvert;
import com.css.txw.sso.enums.OAuth2GrantTypeEnum;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
import com.css.txw.sso.pojo.dto.session.SessionInfo;
import com.css.txw.sso.pojo.vo.oauth2.OAuth2OpenAccessTokenRespVO;
import com.css.txw.sso.pojo.vo.oauth2.OAuth2OpenCheckTokenRespVO;
import com.css.txw.sso.service.oauth2.OAuth2ClientService;
import com.css.txw.sso.service.oauth2.OAuth2GrantService;
import com.css.txw.sso.service.oauth2.OAuth2TokenService;
import com.css.txw.sso.util.HttpUtils;
import com.css.txw.sso.util.OAuth2Utils;
import com.css.txw.sso.util.SecurityFrameworkUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
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 static com.css.ggzc.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;

import static com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.css.ggzc.framework.common.pojo.CommonResult.success;

@Tag(name = "基础OAuth2授权")
@RestController
@RequestMapping("/oauth2")
@Slf4j
public class OAuth2BasicController {

    @Resource
    private OAuth2GrantService oauth2GrantService;
    @Resource
    private OAuth2ClientService oauth2ClientService;

    @Resource
    private OAuth2TokenService oauth2TokenService;


    @PostMapping("/authorize")
    @Operation(summary = "申请授权", description = "OAuth2授权码模式第一步-获取授权码")
    @Parameters({
            @Parameter(name = "response_type", required = true, description = "响应类型", example = "code"),
            @Parameter(name = "client_id", required = true, description = "客户端编号", example = "2f54a1f113d84130a867f559b7279365"),
            @Parameter(name = "redirect_uri", required = true, description = "重定向 URI", example = "http://127.0.0.1"),
            @Parameter(name = "state", example = "1")
    })
    public CommonResult<String> authorize(HttpServletRequest request,
                                          @RequestParam("Response_type") String responseType,
                                          @RequestParam("Client_id") String clientId,
                                          @RequestParam("Redirect_uri") String redirectUri,
                                          @RequestParam(value = "State", required = false) String state) {
        // 校验 responseType 是否满足 code 或者 token 值
        OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType);
        //校验 redirectUri 重定向域名是否合法
        log.info("authorize开始验证redirectUri：{}",redirectUri);
        OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null,
                grantTypeEnum.getGrantType(), null, redirectUri);
        String currentToken = SecurityFrameworkUtils.obtainAuthorization(SsoConstants.COOKIE_TOKEN_KEY,request);
        return success(getAuthorizationCodeRedirect(client, redirectUri, state,currentToken));
    }

    @PostMapping("/token")
    @PermitAll
    @Operation(summary = "获得访问令牌", description = "OAuth2授权码模式第二步-获取访问令牌")
    @Parameters({
            @Parameter(name = "grant_type", required = true, description = "授权类型", example = "authorization_code"),
            @Parameter(name = "code", description = "授权码", example = "2f54a1f113d84130a867f559b7279365"),
            @Parameter(name = "redirect_uri", description = "重定向 URI", example = "http://127.0.0.1"),
            @Parameter(name = "client_id", description = "客户端ID", example = "2f54a1f113d84130a867f559b7279365"),
            @Parameter(name = "client_secret", description = "客户端密钥", example = "2f54a1f113d84130a867f559b7279365"),
            @Parameter(name = "refresh_token", example = "2f54a1f113d84130a867f559b7279365")
    })
    public CommonResult<OAuth2OpenAccessTokenRespVO> postAccessToken(@RequestParam("grant_type") String grantType,
                                                                     @RequestParam(value = "code", required = false) String code,
                                                                     @RequestParam(value = "redirect_uri", required = false) String redirectUri,
                                                                     @RequestParam(value = "client_id") String clientId,
                                                                     @RequestParam(value = "client_secret") String clientSecret,
                                                                     @RequestParam(value = "refresh_token", required = false) String refreshToken) {
        log.info("postAccessToken方法开启，grant_type为"+grantType+"code="+code+"redirect_uri="+redirectUri+"client_id="+clientId+"client_secret="+clientSecret+"refresh_token="+refreshToken);
        //校验授权类型
        OAuth2GrantTypeEnum grantTypeEnum = OAuth2GrantTypeEnum.getByGranType(grantType);
        log.info("授权类型"+ grantTypeEnum);
        if (grantTypeEnum == null) {
            throw exception0(BAD_REQUEST.getCode(), StrUtil.format("未知授权类型({})", grantType));
        }
        if (grantTypeEnum == OAuth2GrantTypeEnum.IMPLICIT) {
            throw exception0(BAD_REQUEST.getCode(), "Token 接口不支持 implicit 授权模式");
        }
        log.info("开始校验客户端");
        //校验客户端
        OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, clientSecret, grantType, null, redirectUri);
        log.info("client返回值为"+client);
        // 2. 根据授权模式，获取访问令牌
        OAuth2AccessTokenDO accessTokenDO;
        switch (grantTypeEnum) {
            case AUTHORIZATION_CODE:
                accessTokenDO = oauth2GrantService.grantAuthorizationCodeForAccessToken(client, code, redirectUri);
                log.info("授权类型为AUTHORIZATION_CODE,accessTokenDO返回值为"+accessTokenDO);
                break;
            case REFRESH_TOKEN:
                accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, client.getClientid());
                log.info("授权类型为REFRESH_TOKEN,accessTokenDO返回值为"+accessTokenDO);
                break;
            default:
                throw new IllegalArgumentException("未知授权类型：" + grantType);
        }
        return success(OAuth2OpenConvert.INSTANCE.convert(accessTokenDO));
    }

    @PostMapping("/check-token")
    @PermitAll
    @Operation(summary = "校验访问令牌")
    @Parameters({
            @Parameter(name = "token", required = true, description = "访问令牌", example = "2f54a1f113d84130a867f559b7279365"),
            @Parameter(name = "client_id", description = "客户端ID", example = "2f54a1f113d84130a867f559b7279365"),
            @Parameter(name = "client_secret", description = "客户端密钥", example = "2f54a1f113d84130a867f559b7279365"),
    })
    public CommonResult<OAuth2OpenCheckTokenRespVO> checkToken(@RequestParam("token") String token,
                                                               @RequestParam(value = "client_id") String clientId,
                                                               @RequestParam(value = "client_secret") String clientSecret) {
        // 校验客户端
        oauth2ClientService.validOAuthClientFromCache(clientId,clientSecret, null, null, null);

        // 校验令牌
        final SessionInfo sessionInfo = oauth2TokenService.checkAccessToken(token);
        Assert.notNull(sessionInfo, "访问令牌不能为空"); // 防御性检查
        return success(OAuth2OpenConvert.INSTANCE.convert2(sessionInfo.getTokenInfo()));
    }


    @PostMapping("/remove-token")
    @PermitAll
    @Operation(summary = "删除访问令牌")
    @Parameters({
            @Parameter(name = "access_token", required = false, description = "访问令牌", example = "2f54a1f113d84130a867f559b7279365"),
            @Parameter(name = "client_id", description = "客户端ID", example = "2f54a1f113d84130a867f559b7279365"),
            @Parameter(name = "client_secret", description = "客户端密钥", example = "2f54a1f113d84130a867f559b7279365"),
    })
    public CommonResult<Boolean> revokeToken(HttpServletRequest request,
                                             @RequestParam(value = "access_token", required = false) String token,
                                             @RequestParam(value = "client_id") String clientId,
                                             @RequestParam(value = "client_secret") String clientSecret) {
        if (GyUtils.isNull(token)){
            token = SecurityFrameworkUtils.obtainAuthorization(SsoConstants.COOKIE_TOKEN_KEY,request);
        }
        // 校验客户端
        OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, clientSecret, null, null, null);
        // 删除访问令牌
        return success(oauth2GrantService.revokeToken(client.getClientid(), token));
    }


/*

    @PostMapping("/refresh-token")
    @PermitAll
    @Operation(summary = "刷新访问令牌", description = "根据刷新令牌获取新的访问令牌")
    @Parameters({
            @Parameter(name = "redirect_uri", description = "重定向 URI", example = "http://127.0.0.1"),
            @Parameter(name = "client_id", description = "客户端ID", example = "2f54a1f113d84130a867f559b7279365"),
            @Parameter(name = "client_secret", description = "客户端密钥", example = "2f54a1f113d84130a867f559b7279365"),
            @Parameter(name = "refresh_token", description = "刷新令牌", example = "2f54a1f113d84130a867f559b7279365")
    })
    public CommonResult<OAuth2OpenAccessTokenRespVO> refreshAccessToken(@RequestParam(value = "redirect_uri") String redirectUri,
                                                                        @RequestParam(value = "client_id") String clientId,
                                                                        @RequestParam(value = "client_secret") String clientSecret,
                                                                        @RequestParam(value = "refresh_token") String refreshToken){

        //校验客户端
        OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, clientSecret, null, null, redirectUri);
        //获取访问令牌
        final OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, client.getClientid());
        return success(OAuth2OpenConvert.INSTANCE.convert(accessTokenDO));
    }

*/

    private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) {
        if (StrUtil.equals(responseType, "code")) {
            return OAuth2GrantTypeEnum.AUTHORIZATION_CODE;
        }
        if (StrUtil.equalsAny(responseType, "token")) {
            return OAuth2GrantTypeEnum.IMPLICIT;
        }
        throw exception0(BAD_REQUEST.getCode(), "response_type 参数值只允许 code 和 token");
    }

    private String getAuthorizationCodeRedirect(OAuth2ClientDO client,String redirectUri, String state,String currentToken) {
        // 1. 创建 code 授权码
        String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(client.getClientid(), redirectUri, state,currentToken);
        // 2. 拼接重定向的 URL
        return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state);
    }

    private String[] obtainBasicAuthorization(HttpServletRequest request) {
        return HttpUtils.obtainBasicAuthorization(request);
    }


}
