# The Okta software accompanied by this notice is provided pursuant to the following terms:
# Copyright © 2025-Present, Okta, Inc.
# 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.
# coding: utf-8

"""
Okta Admin Management

Allows customers to easily access the Okta Management APIs

The version of the OpenAPI document: 5.1.0
Contact: devex-public@okta.com
Generated by OpenAPI Generator (https://openapi-generator.tech)

Do not edit the class manually.
"""  # noqa: E501

import time
from urllib.parse import urlencode, quote

from okta.http_client import HTTPClient
from okta.jwt import JWT


class OAuth:
    """
    This class contains the OAuth actions for the Okta Client.
    """

    OAUTH_ENDPOINT = "/oauth2/v1/token"

    def __init__(self, request_executor, config):
        self._request_executor = request_executor
        self._config = config
        self._access_token = None

    def get_JWT(self):
        """
        Generates JWT using client configuration

        Returns:
            str: Generated JSON Web Token
        """
        org_url = self._config["client"]["orgUrl"]
        client_id = self._config["client"]["clientId"]
        private_key = self._config["client"]["privateKey"]
        # check if kid is provided in config - set if so
        kid = self._config["client"]["kid"] if "kid" in self._config["client"] else None

        return JWT.create_token(org_url, client_id, private_key, kid)

    async def get_access_token(self):
        """
        Retrieves or generates the OAuth access token for the Okta Client

        Returns:
            str, Exception: Tuple of the access token, error that was raised
            (if any)
        """
        # Check if access token has expired or will expire soon
        current_time = int(time.time())
        if self._access_token and hasattr(self, "_access_token_expiry_time"):
            renewal_offset = (
                self._config["client"]["oauthTokenRenewalOffset"] * 60
            )  # Convert minutes to seconds
            if current_time + renewal_offset >= self._access_token_expiry_time:
                self.clear_access_token()

        # Return token if already generated
        if self._access_token:
            return (self._access_token, None)

        # Otherwise create new one
        # Get JWT and create parameters for new Oauth token
        jwt = self.get_JWT()
        parameters = {
            "grant_type": "client_credentials",
            "scope": " ".join(self._config["client"]["scopes"]),
            "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
            "client_assertion": jwt,
        }

        encoded_parameters = urlencode(parameters, quote_via=quote)
        org_url = self._config["client"]["orgUrl"]
        url = f"{org_url}{OAuth.OAUTH_ENDPOINT}?" + encoded_parameters

        # Craft request
        oauth_req, err = await self._request_executor.create_request(
            "POST",
            url,
            form={"client_assertion": jwt},
            headers={
                "Accept": "application/json",
                "Content-Type": "application/x-www-form-urlencoded",
            },
            oauth=True,
        )

        # TODO Make max 1 retry
        # Shoot request
        if err:
            return (None, err)
        _, res_details, res_json, err = await self._request_executor.fire_request(
            oauth_req
        )
        # Return HTTP Client error if raised
        if err:
            return (None, err)

        # Check response body for error message
        parsed_response, err = HTTPClient.check_response_for_error(
            url, res_details, res_json
        )
        # Return specific error if found in response
        if err:
            return (None, err)

        # Otherwise set token and return it
        self._access_token = parsed_response["access_token"]

        # Set token expiry time
        self._access_token_expiry_time = (
            int(time.time()) + parsed_response["expires_in"]
        )
        return (self._access_token, None)

    def clear_access_token(self):
        """
        Clear currently used OAuth access token, probably expired
        """
        self._access_token = None
        self._request_executor._cache.delete("OKTA_ACCESS_TOKEN")
        self._request_executor._default_headers.pop("Authorization", None)
        self._access_token_expiry_time = None
