package org.keycloak.servlet;

import org.jboss.resteasy.plugins.server.servlet.ServletUtil;
import org.jboss.resteasy.spi.ResteasyUriInfo;
import org.keycloak.AbstractOAuthClient;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.URI;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
public class ServletOAuthClient extends AbstractOAuthClient {
    /**
     * Start the process of obtaining an access token by redirecting the browser
     * to the authentication server
     *
     * @param relativePath path relative to context root you want auth server to redirect back to
     * @param request
     * @param response
     * @throws IOException
     */
    public void redirectRelative(String relativePath, HttpServletRequest request, HttpServletResponse response) throws IOException {
        ResteasyUriInfo uriInfo = ServletUtil.extractUriInfo(request, null);
        String redirect = uriInfo.getBaseUriBuilder().path(relativePath).toTemplate();
        redirect(redirect, request, response);
    }


    /**
     * Start the process of obtaining an access token by redirecting the browser
     * to the authentication server
     *
     * @param redirectUri full URI you want auth server to redirect back to
     * @param request
     * @param response
     * @throws IOException
     */
    public void redirect(String redirectUri, HttpServletRequest request, HttpServletResponse response) throws IOException {
        String state = getStateCode();

        URI url = UriBuilder.fromUri(authUrl)
                .queryParam("client_id", clientId)
                .queryParam("redirect_uri", redirectUri)
                .queryParam("state", state)
                .build();
        String stateCookiePath = this.stateCookiePath;
        if (stateCookiePath == null) stateCookiePath = request.getContextPath();
        if (stateCookiePath.equals("")) stateCookiePath = "/";

        Cookie cookie = new Cookie(stateCookieName, state);
        cookie.setSecure(isSecure);
        cookie.setPath(stateCookiePath);
        response.addCookie(cookie);
        response.sendRedirect(url.toString());
    }

    protected String getCookieValue(String name, HttpServletRequest request) {
        if (request.getCookies() == null) return null;

        for (Cookie cookie : request.getCookies()) {
            if (cookie.getName().equals(name)) return cookie.getValue();
        }
        return null;
    }

    protected String getCode(HttpServletRequest request) {
        String query = request.getQueryString();
        if (query == null) return null;
        String[] params = query.split("&");
        for (String param : params) {
            int eq = param.indexOf('=');
            if (eq == -1) continue;
            String name = param.substring(0, eq);
            if (!name.equals("code")) continue;
            return param.substring(eq + 1);
        }
        return null;
    }


    /**
     * Obtain the code parameter from the url after being redirected back from the auth-server.  Then
     * do an authenticated request back to the auth-server to turn the access code into an access token.
     *
     * @param request
     * @return
     * @throws BadRequestException
     * @throws InternalServerErrorException
     */
    public String getBearerToken(HttpServletRequest request) throws BadRequestException, InternalServerErrorException {
        String error = request.getParameter("error");
        if (error != null) throw new BadRequestException(new Exception("OAuth error: " + error));
        String redirectUri = request.getRequestURL().append("?").append(request.getQueryString()).toString();
        String stateCookie = getCookieValue(stateCookieName, request);
        if (stateCookie == null) throw new BadRequestException(new Exception("state cookie not set"));
        // we can call get parameter as this should be a redirect
        String state = request.getParameter("state");
        String code = request.getParameter("code");

        if (state == null) throw new BadRequestException(new Exception("state parameter was null"));
        if (!state.equals(stateCookie)) {
            throw new BadRequestException(new Exception("state parameter invalid"));
        }
        if (code == null) throw new BadRequestException(new Exception("code parameter was null"));
        return resolveBearerToken(redirectUri, code);
    }


}
