"""Configuration helpers for Azure OpenAI-backed workshop demos."""

from __future__ import annotations

import os
from dataclasses import dataclass
from pathlib import Path
from typing import Any

from autogen_core.models import ModelFamily
from dotenv import load_dotenv
from langchain_openai import AzureChatOpenAI, ChatOpenAI
from openai import AzureOpenAI, OpenAI

try:
    from autogen_ext.models.openai import AzureOpenAIChatCompletionClient
except ImportError:  # pragma: no cover - optional at import time
    AzureOpenAIChatCompletionClient = None

try:
    from autogen_ext.models.openai import OpenAIChatCompletionClient
except ImportError:  # pragma: no cover - optional at import time
    OpenAIChatCompletionClient = None


PROJECT_ROOT = Path(__file__).resolve().parent.parent


@dataclass(frozen=True)
class Settings:
    """Store OpenAI settings loaded from the environment."""

    provider: str  # "azure" | "openai"
    openai_model_name: str
    # Azure-specific (populated when provider == "azure")
    openai_azure_endpoint_gpt: str | None
    openai_api_key_gpt: str | None
    openai_api_version_gpt: str | None
    openai_model_version_gpt: str | None
    # Direct OpenAI (populated when provider == "openai")
    openai_api_key: str | None
    # Shared optional
    openai_reasoning: str | None
    langsmith_tracing: bool
    langsmith_api_key: str | None
    langsmith_project: str
    langsmith_endpoint: str


def load_settings() -> Settings:
    """Load OpenAI settings from the local .env file and environment."""
    load_dotenv(PROJECT_ROOT / ".env")
    provider = os.environ.get("OPENAI_PROVIDER", "azure")
    common = dict(
        openai_reasoning=os.environ.get("OPENAI_REASONING") or None,
        langsmith_tracing=os.environ.get("LANGSMITH_TRACING", "").lower() == "true",
        langsmith_api_key=os.environ.get("LANGSMITH_API_KEY"),
        langsmith_project=os.environ.get("LANGSMITH_PROJECT", "workshop-traces"),
        langsmith_endpoint=os.environ.get("LANGSMITH_ENDPOINT", "https://api.smith.langchain.com"),
    )
    if provider == "openai":
        return Settings(
            provider=provider,
            openai_model_name=os.environ["OPENAI_MODEL_NAME"],
            openai_azure_endpoint_gpt=None,
            openai_api_key_gpt=None,
            openai_api_version_gpt=None,
            openai_model_version_gpt=None,
            openai_api_key=os.environ["OPENAI_API_KEY"],
            **common,
        )
    return Settings(
        provider=provider,
        openai_model_name=os.environ["OPENAI_MODEL_NAME"],
        openai_azure_endpoint_gpt=os.environ["OPENAI_AZURE_ENDPOINT_GPT"],
        openai_api_key_gpt=os.environ["OPENAI_API_KEY_GPT"],
        openai_api_version_gpt=os.environ["OPENAI_API_VERSION_GPT"],
        openai_model_version_gpt=os.environ["OPENAI_MODEL_VERSION_GPT"],
        openai_api_key=None,
        **common,
    )


def langsmith_env() -> dict[str, str]:
    """Return the LangSmith environment values used by the workshop."""
    settings = load_settings()
    env = {
        "LANGSMITH_TRACING": str(settings.langsmith_tracing).lower(),
        "LANGSMITH_PROJECT": settings.langsmith_project,
        "LANGSMITH_ENDPOINT": settings.langsmith_endpoint,
    }
    if settings.langsmith_api_key:
        env["LANGSMITH_API_KEY"] = settings.langsmith_api_key
    return env


def build_langchain_llm(
    *,
    temperature: float = 0,
    model_name: str | None = None,
    max_retries: int = 2,
) -> ChatOpenAI:
    """Build the LangChain chat model used across the workshop."""
    settings = load_settings()
    if settings.provider == "openai":
        return ChatOpenAI(
            model=model_name or settings.openai_model_name,
            api_key=settings.openai_api_key,
            temperature=temperature,
            max_retries=max_retries,
        )
    return AzureChatOpenAI(
        model=model_name or settings.openai_model_name,
        api_version=settings.openai_api_version_gpt,
        azure_endpoint=settings.openai_azure_endpoint_gpt,
        api_key=settings.openai_api_key_gpt,
        model_version=settings.openai_model_version_gpt,
        temperature=temperature,
        max_retries=max_retries,
        reasoning_effort=settings.openai_reasoning,
    )


def build_openai_azure_client() -> OpenAI:
    """Build the OpenAI SDK client for the comparison section."""
    settings = load_settings()
    if settings.provider == "openai":
        return OpenAI(api_key=settings.openai_api_key)
    return AzureOpenAI(
        azure_endpoint=settings.openai_azure_endpoint_gpt,
        api_key=settings.openai_api_key_gpt,
        api_version=settings.openai_api_version_gpt,
    )


def build_autogen_model_client(
    *,
    deployment_name: str | None = None,
    model_name: str | None = None,
    temperature: float = 0,
) -> Any:
    """Build the AutoGen model client for the comparison section."""
    if AzureOpenAIChatCompletionClient is None:
        raise ImportError(
            "AutoGen extras are not installed. Install the uv project dependencies first."
        )

    settings = load_settings()
    is_reasoning = bool(settings.openai_reasoning)
    model_info = {
        "vision": False,
        "function_calling": True,
        "json_output": True,
        "structured_output": True,
        "family": ModelFamily.GPT_5,
    }
    extra = {} if is_reasoning else {"temperature": temperature}
    if is_reasoning:
        extra["reasoning_effort"] = settings.openai_reasoning

    if settings.provider == "openai":
        return OpenAIChatCompletionClient(
            model=model_name or settings.openai_model_name,
            api_key=settings.openai_api_key,
            model_info=model_info,
            **extra,
        )
    return AzureOpenAIChatCompletionClient(
        azure_deployment=deployment_name or settings.openai_model_name,
        model=model_name or f"{settings.openai_model_name}-{settings.openai_model_version_gpt}",
        api_version=settings.openai_api_version_gpt,
        azure_endpoint=settings.openai_azure_endpoint_gpt,
        api_key=settings.openai_api_key_gpt,
        model_info=model_info,
        **extra,
    )
