# ruff: noqa: T201
"""Module for the base class of provider setup handlers."""

from abc import ABC, abstractmethod

from yera.config2.dataclasses import (
    CredentialsMap,
    ModelRegistry,
    YeraConfig,
)
from yera.config2.keyring import DevKeyring


class BaseProviderSetup(ABC):
    """Abstract base class for AI provider setup handlers.

    Defines the interface for credential detection, validation, and model
    fetching across different AI providers. Subclasses implement provider-
    specific logic while inheriting common setup workflows.
    """

    def __init__(self, provider_name: str) -> None:
        """Initialise the provider setup handler.

        Args:
            provider_name: Name of the provider (e.g., "anthropic", "openai").
        """
        self.provider_name = provider_name

    def _confirm_ask_for_creds(self) -> bool:
        print(
            f"  Would you like to add your {self.provider_name.title()} API key? (y/n)"
        )

        choice = input(" > ")
        while choice not in ["y", "n"]:
            print("  Please enter either 'y' or 'n'.")
            choice = input(" > ")

        return choice == "y"

    @abstractmethod
    def detect_creds(self) -> dict[str, str] | None:
        """Detect credentials from environment."""
        pass

    @abstractmethod
    def ask_for_creds(self) -> dict[str, str] | None:
        """Prompt user for credentials."""
        pass

    @abstractmethod
    def validate_creds(self, creds: dict[str, str]) -> None:
        """Validate credential format."""
        pass

    @abstractmethod
    def fetch_models(self, creds_map: CredentialsMap) -> ModelRegistry:
        """Fetch available models using provider API."""
        pass

    def automatic_setup(self) -> YeraConfig | None:
        """Attempt automatic setup by detecting credentials from pre-existing data.

        These can be environment variables and configuration files.

        Returns:
            YeraConfig with detected credentials and fetched models, or None
            if automatic detection fails.
        """
        print(f"[AUTO]: Getting credentials for {self.provider_name}")
        try:
            creds = self.detect_creds()
            return self._setup_and_config(creds)
        except Exception as e:
            print(
                f"  An error occurred while attempting set up "
                f"for {self.provider_name} ({type(e).__name__}: {e})"
            )
            print("  Skipping...")
            return None

    def interactive_setup(self) -> YeraConfig | None:
        """Perform interactive setup by prompting user to input information.

        Returns:
            YeraConfig with user-provided credentials and fetched models, or
            None if user declines to provide credentials.
        """
        try:
            creds = self.ask_for_creds()
            return self._setup_and_config(creds)
        except Exception as e:
            print(
                f"  An error occurred while attempting to set up "
                f"for {self.provider_name} ({type(e).__name__}: {e})"
            )
            print("  Skipping...")
            return None

    def _setup_and_config(self, creds: dict[str, str] | None) -> YeraConfig | None:
        if creds is None:
            return None

        self.validate_creds(creds)

        credentials_map = self._add_to_keyring_and_make_map(creds)
        try:
            models = self.fetch_models(credentials_map)
        except Exception as e:
            print(f"  Warning: Could not fetch {self.provider_name} models: {e}")
            print("  Continuing with empty model list.")
            models = ModelRegistry()

        print(f"  Config is updated for {self.provider_name}")
        return YeraConfig(credentials=credentials_map, models=models)

    def _add_to_keyring_and_make_map(self, creds: dict[str, str]) -> CredentialsMap:
        """Standard keyring storage (same for all providers)."""
        for k, v in creds.items():
            key = f"providers.{self.provider_name}.{k}"
            print(f"  Adding {key} to dev keyring.")
            DevKeyring.set(key, v)
        map_dict = {k: f"{self.provider_name}.{k}" for k in creds}
        return CredentialsMap(providers={self.provider_name: map_dict})
