# Copyright 2020 The Johns Hopkins University Applied Physics Laboratory
#
# 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 json
import ssl
from urllib.request import Request, urlopen
from urllib.parse import urlencode
from urllib.error import HTTPError

from . import exceptions

class KeyCloakClient:
    """Client for connecting to Keycloak and using the REST API.

    Client provides a method for issuing requests to the Keycloak REST API and
    a set of methods to simplify Keycloak configuration.

    Context manager examples:

    kc = KeyCloakClient(url)
    with kc.login(username, password):
        kc.method(arguments)

    with KeyCloakClient(url, username, password) as kc:
        kc.method(arguments)
    """
    def __init__(self, url_base, username=None, password=None, client_id='admin-cli', verify_ssl=True):
        """KeyCloakClient constructor

        Args:
            url_base (str) : The base URL to prepend to all request URLs
            verify_ssl (bool) : Whether or not to verify HTTPS certs
        """
        self.url_base = url_base
        self.token = None
        self.username = username
        self.password = password
        self.client_id = client_id

        if self.url_base.startswith("https") and not verify_ssl:
            self.ctx = ssl.create_default_context()
            self.ctx.check_hostname = False
            self.ctx.verify_mode = ssl.CERT_NONE
        else:
            self.ctx = None

    def request(self, url, params=None, headers={}, convert=urlencode, method=None):
        """Make a request to the Keycloak server.

        Args:
            url (str) : REST API URL to query (appended to url_base from constructor)
            params (None|dict) : None or a dict or key values that will be passed
                                 to the convert argument to produce a string
            headers (dict) : Dictionary of HTTP headers
            convert : Function to convert params into a string
                      Defaults to urlencode, taking a dict and making a url encoded string
            method (None|string) : HTTP method to use or None for the default method
                                   based on the different arguments

        Returns:
            (None) : If there is an exception raised
            (dict) : Dictionary containing JSON encoded response
        """
        request = Request(
            self.url_base + url,
            data=None if params is None else convert(params).encode("utf-8"),