import typing
from datetime import datetime, timezone

from pyuspostalservice.clients.base_client import USPSClient
from pyuspostalservice.config import Config

if typing.TYPE_CHECKING:
    from pyuspostalservice.endpoints.usps.AddressesEndpoint import AddressesEndpoint
    from pyuspostalservice.endpoints.usps.TrackingEndpoint import TrackingEndpoint


class USPSAPIClient(USPSClient):
    """
    USPS API client. Handles the connection to the USPS API
    and the configuration of all the available endpoints.
    """

    def __init__(
        self,
        client_id: str,
        secret_key: str,
        scope: str,
    ) -> None:
        """
        Initializes the client with the given credentials.

        Parameters:
            client_id (str): Your USPS API client ID.
            secret_key (str): Your USPS API private key.
            scope (str): The scope of the API access.
        """
        self.client_id: str = client_id
        self.secret_key: str = secret_key
        self.scope: str = scope

        # Grab first access token
        self.access_token: str = self._get_access_token()

    # Initializing endpoints
    @property
    def addresses(self) -> "AddressesEndpoint":
        from pyuspostalservice.endpoints.usps.AddressesEndpoint import AddressesEndpoint

        return AddressesEndpoint(self)

    @property
    def tracking(self) -> "TrackingEndpoint":
        from pyuspostalservice.endpoints.usps.TrackingEndpoint import TrackingEndpoint

        return TrackingEndpoint(self)


    def _get_url(self) -> str:
        """
        Generates and returns the URL for the USPS API endpoints based on the company url and codebase.
        Logs in an obtains an access token.
        Returns:
            str: API URL.
        """
        return f"https://apis-tem.usps.com/"

    def _get_access_token(self, refresh_token: str = "") -> str:
        """
        Performs a request to the USPS API to obtain an access token.
        """
        if refresh_token:
            auth_response = self._make_request(
                "POST",
                f"{self._get_url()}/oauth2/v3/token",
                data={
                    "grant_type": "refresh_token",
                    "client_id": self.client_id,
                    "client_secret": self.secret_key,
                    "scope": self.scope,
                    "refresh_token": refresh_token,},
                headers={"Content-Type": "application/json"},
            )
        else:
            auth_response = self._make_request(
                "POST",
                f"{self._get_url()}oauth2/v3/token",
                data={
                    "grant_type": "client_credentials",
                    "client_id": self.client_id,
                    "client_secret": self.secret_key,
                    "scope": self.scope,},
                headers={"Content-Type": "application/json"},
            )
        auth_resp_json = auth_response.json()
        token = auth_resp_json["access_token"]
        self.token_expiry_time = datetime.fromtimestamp(auth_resp_json["issued_at"] / 1000 + auth_resp_json["expires_in"], tz=timezone.utc)
        return token

    def _refresh_access_token_if_necessary(self):  # noqa: ANN202
        if datetime.now(tz=timezone.utc) > self.token_expiry_time:
            self.access_token = self._get_access_token(refresh=True)

    def _get_headers(self) -> dict[str, str]:
        """
        Generates and returns the headers required for making API requests. The access token is refreshed if necessary before returning.

        Returns:
            dict[str, str]: Dictionary of headers including Content-Type, Client ID, and Authorization.
        """
        return {
            "Content-Type": "application/json",
            "Accept": "application/json",
            "Authorization": f"Bearer {self.access_token}",
        }
