from ..errors import (
    MissingRequiredOauthArguments,
    NoScopesProvided
)

class OauthSession:
    """A class for managing an oauth session URL.
    
    Upon instantiating this class, you must call the set_scopes()
    method to select the scopes that have been selected on the 
    developer portal.
    
    Kwargs:
    -   `client_id` = The client ID from the dev portal.
    -   `redirect_uri` = The redirect URI from the dev portal.
    -   `response_type` = The response type. This should always be "code".
    -   `state` = The optional return state. Used for sessions.
    """
    BASE: str = "https://wants-to.party/oauth/authorize?"
    def __init__(
            self,
            *,
            client_id: str,
            redirect_uri: str,  
            response_type: str = "code",
            state: str = None
        ) -> None:
        self.client_id: str = client_id
        self.scopes: dict = {}
        self.redirect_uri: str = redirect_uri
        self.response_type: str = response_type
        self.state: str = state
    
    @classmethod
    def set_scopes(
            self,
            *,
            user_read: bool = False,
            user_email: bool = False,
            files_read: bool = False,
            files_write: bool = False,
            files_delete: bool = False
        ):
        """A required function that sets the scope 
        arguments to be included in the oauth URL.

        Kwargs corresponding to their scope:
        -   user_read = user.read
        -   user_email = user.email
        -   files_read = files.read
        -   files_write = files.write
        -   files_delete = files.delete

        """
        self.scopes = locals()
    
    @classmethod
    def _gen_url_friendly_scopes(self) -> str:
        final = []
        if self.scopes.get("user_read"):
            final.append("user.read")
        if self.scopes.get("user_email"):
            final.append("user.email")
        if self.scopes.get("files_read"):
            final.append("files.read")
        if self.scopes.get("files_write"):
            final.append("files.write")
        if self.scopes.get("files_delete"):
            final.append("files.delete")
        
        return "+".join(final)

    @classmethod
    def _check_required_arguments_exist(self) -> None:
        if not self.scopes:
            raise NoScopesProvided(
                "No scopes selected. Call the set_scopes() function to " \
                "set the scopes."
            )
        if not all(
            (self.client_id,
            self.redirect_uri,
            self.response_type)
        ):
            raise MissingRequiredOauthArguments(
                "You must provide a client_id, redirect_uri, and response_type"
            )

    @classmethod
    def _assemble_url(self) -> str:
        self._check_required_arguments_exist()

        client_id = self.client_id
        scopes = self._gen_url_friendly_scopes()
        redirect_uri = self.redirect_uri
        response_type = self.response_type
        state = "&" + self.state if self.state else ""

        sep = "&"

        return self.BASE + client_id + sep + scopes + sep + \
            redirect_uri + response_type + state
    
    @property
    def generated_url(self) -> str:
        """The final URL that's been generated by
        the information provided."""
        return self._assemble_url()

    @property
    def scopes(self) -> dict:
        """The scopes you have selected."""
        return self.scopes
