#!/usr/bin/env python3

# from byfrost.gcs.gcs import GoogleCloudStorage
from .gcs.gcs import GoogleCloudStorage
from .s3.s3 import SimpleStorageService
from .shared.types.dataclass import bridge
from .shared.errors.interface import BifrostError
from typing import Tuple, Union
from .shared.errors import constant as errors
from .shared.config import provider, url
from .shared.errors import loga
from google.cloud import storage
import requests
from .pinata.pinata import PinataCloud
from .shared.request import builder
import boto3


def new_rainbow_bridge(
    bc: bridge.BridgeConfig,
) -> Tuple[bridge.RainbowBridge, BifrostError]:
    """Returns a new Rainbow Bridge for shipping files to your specified cloud storage service and an error if one occurs."""
    if bc is None:
        return None, BifrostError("config is none", errors.ErrInvalidConfig)

    # verify that the config is derived from BridgeConfig
    if not isinstance(bc, bridge.BridgeConfig):
        return None, BifrostError(
            "invalid config type: {}".format(type(bc)), errors.ErrInvalidConfig
        )

    # verify that the config dataclass has a valid provider
    if bc.provider is None or bc.provider == "":
        return None, BifrostError("no provider specified", errors.ErrInvalidProvider)

    # verify that the provider is valid
    if bc.provider not in provider.providers:
        return None, BifrostError(
            "invalid provider: {}".format(bc.provider), errors.ErrInvalidProvider
        )

        # verify that the config dataclass has a valid bucket
    if bc.default_bucket is None or bc.default_bucket == "":
        # some providers might not require a bucket
        # just log a warning
        if bc.enable_debug is True:
            loga.warn(
                "WARN: No bucket specified for provider: {}. This might cause errors or require you to specify a bucket for each operation.".format(
                    bc.provider
                )
            )

    # create a new bridge based on the provider
    if bc.provider == provider.GoogleCloudStorage:
        return new_google_cloud_storage(bc)

    if bc.provider == provider.PinataCloud:
        return new_pinata_cloud_storage(bc)

    if bc.provider == provider.SimpleStorageService:
        return new_simple_storage_service(bc)

    return None, BifrostError(
        "invalid provider: {}".format(bc.provider), errors.ErrInvalidProvider
    )


def new_google_cloud_storage(
    bc: bridge.BridgeConfig,
) -> Tuple[bridge.RainbowBridge, BifrostError]:
    """Returns a new client for Google Cloud Storage."""
    client: storage.Client = None

    # attempting to authenticate with credentials file
    if bc.credentials_file is not None and bc.credentials_file != "":
        try:
            client = storage.Client.from_service_account_json(bc.credentials_file)
        except Exception as e:
            return None, BifrostError(str(e), errors.ErrUnauthorized)
        return GoogleCloudStorage(bc, client), None
    else:
        # attempt to authenticate without credentials file
        try:
            client = storage.Client()
        except Exception as e:
            return None, BifrostError(str(e), errors.ErrUnauthorized)

        # return a new Google Cloud Storage client (the bifrost version)
        return GoogleCloudStorage(bc, client), None


def new_pinata_cloud_storage(
    bc: bridge.BridgeConfig,
) -> Tuple[bridge.RainbowBridge, BifrostError]:
    """new_pinata_cloud_storage returns a new client for Pinata Cloud Storage"""
    # TODO: add support for API key and API secret
    client: builder.new_client = None

    # attempting to authenticate with JWT token
    if bc.pinata_jwt is not None and bc.pinata_jwt != "":
        try:
            client = builder.new_client(
                url.URLPinataAuth, bc.pinata_jwt, bc.default_timeout
            )
            pc = PinataCloud(bc, client)
            error = pc.preflight()
            if error is not None:
                return None, error
        except Exception as e:
            return None, BifrostError(str(e), errors.ErrUnauthorized)
        return pc, None
    return None, BifrostError("pinata JWT is required", errors.ErrUnauthorized)

    # return a new Pinata Cloud client (the bifrost version)
    return PinataCloud(bc, client), None


def new_simple_storage_service(
    bc: bridge.BridgeConfig,
) -> Tuple[bridge.RainbowBridge, BifrostError]:
    """new_simple_storage_service returns a new client for Simple Storage Service."""
    client: boto3.session.resource("s3")

    # attempting to authenticate with access and secret key
    if (
        bc.access_key is not None
        and bc.access_key != ""
        and bc.secret_key is not None
        and bc.secret_key != ""
        and bc.region is not None
        and bc.region != ""
    ):
        try:
            session = boto3.session.Session(access_key, secret_key, region_name)
            client = session.resource("s3")
        except Exception as e:
            return None, BifrostError(str(e), errors.ErrUnauthorized)
        return SimpleStorageService(bc, client), None
    else:
        # attempt to authenticate without access key and secret key
        try:
            session = boto3.session.Session()
            client = session.resource("s3")
        except Exception as e:
            return None, BifrostError(str(e), errors.ErrUnauthorized)

        # return a new Amazon Simple Storage Service client (the bifrost version)
        return SimpleStorageService(bc, client), None
