
from io import StringIO
from loguru import logger
from typing import TypeVar

from gcloud.aio.auth import Token
from gcloud.aio.storage import Storage
from pymongo import AsyncMongoClient
from pymongo.asynchronous.database import AsyncDatabase

from bb_integrations_lib.gravitate.base_api import BaseAPI
from bb_integrations_lib.gravitate.pe_api import GravitatePEAPI
from bb_integrations_lib.gravitate.rita_api import GravitateRitaAPI
from bb_integrations_lib.gravitate.sd_api import GravitateSDAPI
from bb_integrations_lib.provider.api.anova.client import AnovaApiClient
from bb_integrations_lib.provider.api.dtn.client import DTNClient
from bb_integrations_lib.provider.api.quicktrip.client import QTApiClient
from bb_integrations_lib.provider.api.samsara.client import SamsaraClient
from bb_integrations_lib.provider.api.pc_miler.client import PCMilerClient
from bb_integrations_lib.provider.api.truefill.client import TrueFillApiClient
from bb_integrations_lib.provider.api.warren_rogers.client import WarrenRogersClient
from bb_integrations_lib.provider.ftp.client import FTPIntegrationClient
from bb_integrations_lib.provider.imap.client import IMAPClient
from bb_integrations_lib.provider.sqlserver.client import SQLServerClient
from bb_integrations_lib.secrets import SecretProvider, AnyCredential, IMAPCredential
from bb_integrations_lib.secrets.credential_models import (
    DTNCredential,
    FTPCredential,
    GoogleCredential,
    MongoDBCredential,
    PCMilerCredential,
    SQLServerCredential,
    WarrenRogersCredential,
    TrueFillCredential,
)

T = TypeVar("T", bound=BaseAPI)


def _log_creation(func):
    async def log(self, secret_name: str, *args, **kwargs):
        ret = await func(self, secret_name, *args, **kwargs)
        if self.log_creations:
            logger.debug(f"Made {type(ret).__name__} from '{secret_name}'")
        return ret

    return log


class APIFactory:
    """Builds API or client instances from named secrets using a SecretProvider."""

    def __init__(self, provider: SecretProvider, log_creations: bool = True):
        self.provider = provider
        self.log_creations = log_creations

    async def _make(self, secret_name: str, api: type[T]) -> T:
        return api.from_credential(
            await self.provider.get_secret(secret_name, AnyCredential)
        )

    @_log_creation
    async def sd(self, secret_name: str) -> GravitateSDAPI:
        return await self._make(secret_name, GravitateSDAPI)

    @_log_creation
    async def rita(self, secret_name: str) -> GravitateRitaAPI:
        return await self._make(secret_name, GravitateRitaAPI)

    @_log_creation
    async def pe(self, secret_name: str) -> GravitatePEAPI:
        return await self._make(secret_name, GravitatePEAPI)

    @_log_creation
    async def ftp(self, secret_name: str) -> FTPIntegrationClient:
        return FTPIntegrationClient(
            await self.provider.get_secret(secret_name, FTPCredential)
        )

    @_log_creation
    async def gcloud_storage(self, secret_name: str) -> Storage:
        secret = await self.provider.get_secret(secret_name, GoogleCredential)
        as_json = secret.model_dump_json()
        return Storage(
            token=Token(
                service_file=StringIO(as_json),
                scopes=["https://www.googleapis.com/auth/cloud-platform"],
            )
        )

    @_log_creation
    async def mongo_db(self, secret_name: str) -> AsyncDatabase:
        secret = await self.provider.get_secret(secret_name, MongoDBCredential)
        return AsyncMongoClient(secret.mongo_conn_str)[secret.mongo_db_name]

    @_log_creation
    async def imap(self, secret_name: str) -> IMAPClient:
        return IMAPClient(await self.provider.get_secret(secret_name, IMAPCredential))

    @_log_creation
    async def qt(self, secret_name: str) -> QTApiClient:
        return await self._make(secret_name, QTApiClient)

    @_log_creation
    async def sql(self, secret_name: str) -> SQLServerClient:
        secret = await self.provider.get_secret(secret_name, SQLServerCredential)
        return SQLServerClient(
            server=secret.server,
            database=secret.database,
            username=secret.username,
            password=secret.password,
            driver=secret.driver,
        )

    @_log_creation
    async def samsara(self, secret_name: str) -> SamsaraClient:
        return await self._make(secret_name, SamsaraClient)

    @_log_creation
    async def dtn(self, secret_name: str) -> DTNClient:
        secret = await self.provider.get_secret(secret_name, DTNCredential)
        return DTNClient(
            username=secret.username,
            web_service_key=secret.web_service_key,
            api_key=secret.api_key,
            base_url="https://api.dtn.com/fuelsuite/allocationtracker/",
        )

    @_log_creation
    async def warren_rogers(self, secret_name: str) -> WarrenRogersClient:
        secret = await self.provider.get_secret(secret_name, WarrenRogersCredential)
        return WarrenRogersClient.from_credential(secret)

    @_log_creation
    async def truefill(self, secret_name: str) -> TrueFillApiClient:
        secret = await self.provider.get_secret(secret_name, TrueFillCredential)
        return TrueFillApiClient(
            email=secret.email,
            password=secret.password,
            base_url=secret.host,
        )

    @_log_creation
    async def pc_miler(self, secret_name: str) -> PCMilerClient:
        secret = await self.provider.get_secret(secret_name, PCMilerCredential)
        return PCMilerClient.from_credential(secret)

    @_log_creation
    async def anova(self, secret_name: str) -> AnovaApiClient:
        return await self._make(secret_name, AnovaApiClient)
