package net.northking.cctp.element.internal.protocol.http.auth.oauth2;

import net.northking.cctp.element.internal.protocol.http.HttpClient;
import org.apache.http.Header;
import org.apache.http.HttpRequest;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.impl.auth.AuthSchemeBase;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BufferedHeader;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.CharArrayBuffer;

/**
 * OAuth2Scheme
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
public class OAuth2Scheme extends AuthSchemeBase {

    private static final String UNINITIATED = "UNINITIATED";
    private static final String CHALLENGE_RECEIVED = "CHALLENGE_RECEIVED";
    private static final String FAILED = "FAILED";
    private static final String GENERATED = "GENERATED";
    private HttpContext httpContext;
    private String state;
    private String challenge;
    private OAuth2Client.Token token;

    public OAuth2Scheme(HttpContext httpContext) {
        this.httpContext = httpContext;
        this.state = UNINITIATED;
    }

    @Override
    protected void parseChallenge(CharArrayBuffer buffer, int beginIndex, int len) throws MalformedChallengeException {
        this.challenge = buffer.substringTrimmed(beginIndex, len);
        if (this.challenge.isEmpty()) {
            if (this.state.equals(UNINITIATED)) {
                this.state = CHALLENGE_RECEIVED;
            } else {
                this.state = FAILED;
            }
        }
    }

    @Override
    public Header authenticate(Credentials credentials, HttpRequest httpRequest) throws AuthenticationException {
        try {
            OAuth2Credentials bc = (OAuth2Credentials) credentials;
            OAuth2Client oAuth2Client = new OAuth2Client((CloseableHttpClient) httpContext.getAttribute(HttpClient.HTTP_AUTH_CLIENT));
            if (token == null) {
                token = oAuth2Client.requestToken(bc);
            } else if (token.isExpire()) {
                token = oAuth2Client.refreshToken(bc, token);
            }
            String accessToken = token.getAccess_token();
            CharArrayBuffer buffer = new CharArrayBuffer(32);
            buffer.append(String.format("Authorization: %s %s", bc.getHeaderPrefix(), accessToken));
            this.state = GENERATED;
            return new BufferedHeader(buffer);
        } catch (Exception e) {
            this.state = FAILED;
            throw new AuthenticationException("OAth2授权失败", e);
        }
    }

    @Override
    public String getSchemeName() {
        return "oauth2";
    }

    @Override
    public String getParameter(String s) {
        return null;
    }

    @Override
    public String getRealm() {
        return null;
    }

    @Override
    public boolean isConnectionBased() {
        return true;
    }

    @Override
    public boolean isComplete() {
        return FAILED.equals(this.state) || GENERATED.equals(this.state);
    }

}
