from datetime import datetime, timedelta
from typing import Optional, cast
import backoff
import requests

from arcane.core import BaseAccount, BadRequestError
from arcane.datastore import Client as DatastoreClient

from .const import SNAPCHAT_SERVER_URL, SNAPCHAT_TOKEN_URL
from .exceptions import SnapchatAuthError, SnapchatApiError
from .lib import get_snapchat_account, get_snapchat_user_credentials


class SnapchatClient:
    def __init__(
        self,
        gcp_service_account: str,
        client_id: str,
        client_secret: str,
        base_account: Optional[BaseAccount] = None,
        user_email: Optional[str] = None,
        clients_service_url: Optional[str] = None,
        firebase_api_key: Optional[str] = None,
        gcp_credentials_path: Optional[str] = None,
        datastore_client: Optional[DatastoreClient] = None,
        gcp_project: Optional[str] = None,
        auth_enabled: bool = True
    ) -> None:

        self._client_id = client_id
        self._client_secret = client_secret
        self._access_token: Optional[str] = None
        self._token_expiry: Optional[datetime] = None

        creator_email = None

        if gcp_service_account and (base_account or user_email):
            if user_email:
                creator_email = user_email
            else:
                base_account = cast(BaseAccount, base_account)
                snapchat_account = get_snapchat_account(
                    base_account=base_account,
                    clients_service_url=clients_service_url,
                    firebase_api_key=firebase_api_key,
                    gcp_service_account=gcp_service_account,
                    auth_enabled=auth_enabled
                )

                creator_email = cast(str, snapchat_account['creator_email'])

            if creator_email is None:
                raise BadRequestError('creator_email should not be None while using user access protocol')

            credentials = get_snapchat_user_credentials(
                user_email=creator_email,
                gcp_credentials_path=gcp_credentials_path,
                gcp_project=gcp_project,
                datastore_client=datastore_client
            )

            self._refresh_token = credentials['refresh_token']
        else:
            raise BadRequestError('gcp_service_account and (base_account or user_email) should be provided to initialize SnapchatClient')

    @backoff.on_exception(backoff.expo, requests.exceptions.HTTPError, max_tries=3)
    def _refresh_access_token(self) -> None:
        """Refresh the access token using the stored refresh token."""
        response = requests.post(
            SNAPCHAT_TOKEN_URL,
            data={
                "grant_type": "refresh_token",
                "client_id": self._client_id,
                "client_secret": self._client_secret,
                "refresh_token": self._refresh_token,
            },
            headers={"Content-Type": "application/x-www-form-urlencoded"}
        )

        if response.status_code == 401:
            raise SnapchatAuthError(f"Failed to refresh access token: {response.text}")

        response.raise_for_status()

        response_data = response.json()
        self._access_token = response_data.get('access_token')
        expires_in = response_data.get('expires_in', 3600)
        self._token_expiry = datetime.now() + timedelta(seconds=expires_in)

    def _ensure_valid_token(self) -> None:
        """Ensure we have a valid access token, refreshing if needed."""
        if self._access_token is None or self._token_expiry is None or datetime.now() >= self._token_expiry:
            self._refresh_access_token()

    @backoff.on_exception(backoff.expo, requests.exceptions.HTTPError, max_tries=5)
    def _make_request(self, endpoint: str, method: str, params: Optional[dict] = None, headers: Optional[dict] = None, **kwargs) -> dict:
        """Send a request to Snapchat API with automatic token refresh."""
        self._ensure_valid_token()

        default_headers = {"Authorization": f"Bearer {self._access_token}"}
        if headers:
            default_headers.update(headers)

        response = requests.request(
            method=method,
            url=f"{SNAPCHAT_SERVER_URL}{endpoint}",
            headers=default_headers,
            params=params,
            **kwargs
        )
        response.raise_for_status()

        response_data = response.json()

        if response_data.get('request_status') != 'SUCCESS':
            error_code = response_data.get('error_code', '')
            if error_code in ['E3002', 'E3003']:  # E3002: unauthorized access, E3003: resource not found or no permission
                raise SnapchatAuthError(f"{response_data.get('debug_message')}")
            raise SnapchatApiError(f"{response_data.get('debug_message')}")

        return response_data

    def get_ad_account_info(self, ad_account_id: str) -> dict:
        """Get ad account info.

        GET /v1/adaccounts/{ad_account_id}
        """
        response = self._make_request(
            endpoint=f"/adaccounts/{ad_account_id}",
            method="GET"
        )
        adaccounts = response.get('adaccounts', [])
        if not adaccounts:
            raise SnapchatApiError(f"No ad account found for id: {ad_account_id}")

        return adaccounts[0].get('adaccount', {})

    def get_organizations_with_ad_accounts(self) -> list[dict]:
        """Get all organizations with their ad accounts for the authenticated user.

        GET /v1/me/organizations?with_ad_accounts=true
        """
        response = self._make_request(
            endpoint="/me/organizations",
            method="GET",
            params={"with_ad_accounts": "true"}
        )
        return response.get('organizations', [])
