from typing import Optional

from arcane.datastore import Client as DatastoreClient
from arcane.core import BadRequestError, BaseAccount, ALL_CLIENTS_RIGHTS, UserRightsEnum, RightsLevelEnum
from arcane.requests import call_get_route

from .const import SNAPCHAT_OAUTH_CREDENTIALS_KIND


def get_snapchat_account(
    base_account: BaseAccount,
    clients_service_url: Optional[str] = None,
    firebase_api_key: Optional[str] = None,
    gcp_service_account: Optional[str] = None,
    auth_enabled: bool = True
) -> dict:
    """Fetch Snapchat account details using the base account information.

    Args:
        base_account (BaseAccount): The base account information to fetch the Snapchat account.
        clients_service_url (str, optional): The URL of the clients service to fetch the Snapchat account.
        firebase_api_key (str, optional): The Firebase API key to authenticate with the clients service.
        gcp_service_account (str, optional): The path to the GCP service account JSON file to authenticate with the clients service.
        auth_enabled (bool, optional): Whether authentication is enabled for the clients service. Defaults to True.

    Raises:
        BadRequestError: If the Snapchat account cannot be fetched due to missing parameters or invalid.

    Returns:
        dict: The Snapchat account details.

    """

    if not (clients_service_url and firebase_api_key and gcp_service_account):
        raise BadRequestError('clients_service_url or firebase_api_key or gcp_service_account should not be None if snapchat account is not provided')

    url = f"{clients_service_url}/api/snapchat-account?account_id={base_account['id']}&client_id={base_account['client_id']}"
    accounts = call_get_route(
        url,
        firebase_api_key,
        claims={'features_rights': { UserRightsEnum.AMS_GTP: RightsLevelEnum.VIEWER }, 'authorized_clients': [ALL_CLIENTS_RIGHTS]},
        auth_enabled=auth_enabled,
        credentials_path=gcp_service_account
    )
    if len(accounts) == 0:
        raise BadRequestError(f'Error while getting snapchat account with: {base_account}. No account corresponding.')
    elif len(accounts) > 1:
        raise BadRequestError(f'Error while getting snapchat account with: {base_account}. Several account corresponding: {accounts}')

    return accounts[0]


def get_snapchat_user_credentials(
    user_email: str,
    gcp_credentials_path: Optional[str],
    gcp_project: Optional[str],
    datastore_client: Optional[DatastoreClient]
):
    """Retrieve Snapchat user credentials (refresh_token) from Datastore.

    Args:
        user_email (str): The email of the user whose credentials are to be retrieved.
        gcp_credentials_path (str, optional): The path to the GCP service account JSON file to authenticate with Datastore.
        gcp_project (str, optional): The GCP project ID where the Datastore is located.
        datastore_client (DatastoreClient, optional): An instance of DatastoreClient to interact with Datastore. If not provided, a new client will be created using gcp_credentials_path and gcp_project.

    Raises:
        BadRequestError: If the Snapchat user credentials cannot be fetched due to missing parameters or invalid.

    """

    if not datastore_client:
        if not gcp_credentials_path and not gcp_project:
            raise BadRequestError('gcp_credentials_path or gcp_project should not be None if datastore_client is not provided')
        datastore_client = DatastoreClient.from_service_account_json(gcp_credentials_path, project=gcp_project)

    query = datastore_client.query(kind=SNAPCHAT_OAUTH_CREDENTIALS_KIND).add_filter('email', '=', user_email)
    users_credential = list(query.fetch())
    if len(users_credential) == 0:
        raise BadRequestError(f'Error while getting snapchat user credentials with mail: {user_email}. No entity corresponding.')
    elif len(users_credential) > 1:
        raise BadRequestError(f'Error while getting snapchat user credentials with mail: {user_email}. Several entities corresponding: {users_credential}')

    return users_credential[0]
