from json import JSONDecodeError

from pygeai.core.base.clients import BaseClient
from pygeai.core.common.exceptions import MissingRequirementException
from pygeai.core.services.rest import ApiService
from pygeai.gam.endpoints import GET_ACCESS_TOKEN_V2, GET_USER_INFO_V2


class GAMClient(BaseClient):

    def get_access_token(
            self,
            client_id: str = None,
            client_secret: str = None,
            grant_type: str = "password",
            authentication_type_name: str = "local",
            scope: str = "gam_user_data",
            username: str = None,
            password: str = None,
            initial_properties: dict = None,
            repository: str = None,
            request_token_type: str = "OAuth"
    ):
        """
        Retrieves an access token by sending a POST request to the GAM OAuth 2.0 access token endpoint.

        :param client_id: str - Application Client ID. Required.
        :param client_secret: str - Application Client Secret. Required.
        :param grant_type: str - Grant type for authentication. Must be "password". Defaults to "password".
        :param authentication_type_name: str - Authentication type name. Defaults to "local".
        :param scope: str - Scope of the user account to access (e.g., "gam_user_data+gam_user_roles").
            Defaults to "gam_user_data".
        :param username: str - Username of the user to be authenticated. Required.
        :param password: str - Password of the user to be authenticated. Required.
        :param initial_properties: dict - User custom properties array (e.g., [{"Id":"Company","Value":"GeneXus"}]). Optional.
        :param repository: str - Repository identifier, used only if IDP is multitenant. Optional.
        :param request_token_type: str - Determines the token type to return and security policy.
            Options are "OAuth" or "Web". Defaults to "OAuth".
        :return: dict or str - Access token response containing access_token, token_type, expires_in,
            refresh_token, scope, and user_guid; returns raw text if JSON parsing fails.
        """
        data = {
            "grant_type": grant_type,
            "authentication_type_name": authentication_type_name,
            "scope": scope,
            "request_token_type": request_token_type
        }
        if client_id is not None:
            data["client_id"] = client_id
        if client_secret is not None:
            data["client_secret"] = client_secret
        if username is not None:
            data["username"] = username
        if password is not None:
            data["password"] = password
        if initial_properties is not None:
            data["initial_properties"] = initial_properties
        if repository is not None:
            data["repository"] = repository

        headers = {
            "Content-Type": "application/x-www-form-urlencoded"
        }
        response = self.api_service.post(
            endpoint=GET_ACCESS_TOKEN_V2,
            data=data,
            headers=headers,
            form=True
        )
        try:
            result = response.json()
        except JSONDecodeError as e:
            result = response.text

        return result

    def get_user_info(
            self,
            access_token: str
    ):
        """
        Retrieves user information by sending a GET request to the GAM OAuth 2.0 userinfo endpoint.

        :param access_token: str - Access token obtained from the access_token endpoint. Required.
        :return: dict or str - User information response containing guid, username, email, and other user
            details based on requested scopes; returns raw text if JSON parsing fails.
        """
        headers = {
            "Authorization": access_token,
            "Content-Type": "application/x-www-form-urlencoded"
        }
        response = self.api_service.get(
            endpoint=GET_USER_INFO_V2,
            headers=headers
        )
        try:
            result = response.json()
        except JSONDecodeError as e:
            result = response.text

        return result

    def refresh_access_token(
            self,
            client_id: str = None,
            client_secret: str = None,
            grant_type: str = "refresh_token",
            refresh_token: str = None
    ):
        """
        Refreshes an access token by sending a GET request to the GAM OAuth 2.0 access token endpoint.

        :param client_id: str - Application Client ID. Required.
        :param client_secret: str - Application Client Secret. Required.
        :param grant_type: str - Grant type for authentication. Must be "refresh_token". Defaults to "refresh_token".
        :param refresh_token: str - Refresh token obtained from a previous access token response. Required.
        :return: dict or str - New access token response containing access_token, token_type, expires_in,
            refresh_token, scope, and user_guid; returns raw text if JSON parsing fails.
        """
        data = {
            "grant_type": grant_type,
        }
        if client_id is not None:
            data["client_id"] = client_id
        if client_secret is not None:
            data["client_secret"] = client_secret
        if refresh_token is not None:
            data["refresh_token"] = refresh_token

        headers = {
            "Content-Type": "application/x-www-form-urlencoded"
        }
        response = self.api_service.post(
            endpoint=GET_ACCESS_TOKEN_V2,
            data=data,
            headers=headers,
            form=True
        )
        try:
            result = response.json()
        except JSONDecodeError as e:
            result = response.text

        return result

    def get_authentication_types(self):
        response = self.api_service.get(
            endpoint=GET_ACCESS_TOKEN_V2,
            params={},
        )
        try:
            result = response.json()
        except JSONDecodeError as e:
            result = response.text

        return result
