# ruff: noqa: T201
"""Configuration and credentials import handler for OpenAI."""

import os
import re

from openai import OpenAI
from openai.types import Model

from yera.config2.dataclasses import CredentialsMap, LLMConfig, ModelRegistry
from yera.config2.keyring import DevKeyring
from yera.config2.setup_handlers.base import BaseProviderSetup


class OpenAISetup(BaseProviderSetup):
    """Setup handler for OpenAI provider credentials and models.

    Manages credential detection, validation, and model fetching for the
    OpenAI API. Supports both automatic import from environment variables
    and interactive credential entry.
    """

    def __init__(self):
        """Initialise the OpenAI setup handler."""
        super().__init__("openai")

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

        Returns:
            Dictionary with "api_key" if OPENAI_API_KEY environment variable
            is set, otherwise None.
        """
        api_key = os.environ.get("OPENAI_API_KEY")
        if api_key:
            return {"api_key": api_key}
        return None

    def ask_for_creds(self) -> dict[str, str] | None:
        """Prompt user to enter OpenAI API credentials interactively.

        Returns:
            Dictionary with "api_key" if user provides credentials, otherwise
            None if user declines to enter credentials.
        """
        ask = self._confirm_ask_for_creds()
        if ask:
            print("  Please enter your OpenAI API Key:")
            api_key = input(" > ")
            return {"api_key": api_key}
        return None

    def validate_creds(self, creds: dict[str, str]) -> None:
        """Validate OpenAI API key format.

        Args:
            creds: Dictionary containing "api_key" to validate.

        Raises:
            ValueError: If the API key format is invalid (wrong prefix, invalid
                characters, appears incomplete, or is an Anthropic key).
        """
        api_key = creds["api_key"]

        pattern = r"^sk-[A-Za-z0-9_-]+$"

        if not api_key.startswith("sk-"):
            raise ValueError(
                "Invalid API key format. OpenAI API keys should start with 'sk-'\n"
                "Please ensure you've copied the complete API key from the OpenAI Platform."
            )

        if api_key.startswith("sk-ant-"):
            raise ValueError(
                "This appears to be an Anthropic API key. OpenAI keys start with 'sk-' but not 'sk-ant-'\n"
                "Please ensure you're using an OpenAI API key from platform.openai.com."
            )

        if not re.match(pattern, api_key):
            raise ValueError(
                "Invalid API key format. Key contains invalid characters.\n"
                "Please ensure you've copied the complete API key from the OpenAI Platform."
            )

        if len(api_key) < 40:
            raise ValueError(
                "API key appears too short. Please ensure you've copied the complete key."
            )

    def fetch_models(self, creds_map: CredentialsMap) -> ModelRegistry:
        """Fetch available chat models from OpenAI API.

        Retrieves the list of available models and filters for just language models.

        Args:
            creds_map: Credentials map containing OpenAI provider credentials.

        Returns:
            ModelRegistry containing LLMConfig entries for all available
            OpenAI chat models.
        """
        creds = {
            k: DevKeyring.get(f"providers.{v}")
            for k, v in creds_map.providers["openai"].items()
        }

        client = OpenAI(**creds)

        # OpenAI returns all model types - filter to chat models only
        model_list = client.models.list()

        # Filter to GPT chat models and reasoning models
        chat_models = [
            m
            for m in model_list.data
            if m.id.startswith(("gpt-5", "gpt-4", "gpt-3", "o1-", "o3-"))
        ]

        def _make_model_cfg(model: Model) -> LLMConfig:
            # OpenAI uses the model ID as display name
            model_id = model.id.lower().replace(" ", "-").replace(".", "-")
            return LLMConfig(
                id=f"openai.{model_id}".lower(),
                display_name=model.id,
                credentials="openai",
                provider="openai",
                interface="openai-sdk",
                model_id=model.id,
            )

        model_configs = [_make_model_cfg(model) for model in chat_models]
        names = "  \n   * ".join(m.display_name for m in model_configs)
        print(f"  Adding {len(model_configs)} LLMs from OpenAI:\n   * {names}")

        return ModelRegistry(
            llm={m.id: m for m in model_configs},
        )
