import json
import os
from dataclasses import dataclass

# ─────────────────────────────────────────────────────────────────────────────
# Load Centralized LLM Provider Configuration
# ─────────────────────────────────────────────────────────────────────────────


def _load_llm_config():
    """Load models from centralized JSON file."""
    # Find root directory (where llm_providers.json lives)
    current_dir = os.path.dirname(os.path.abspath(__file__))
    root_dir = os.path.abspath(os.path.join(current_dir, "../../"))
    config_path = os.path.join(root_dir, "llm_providers.json")

    try:
        if os.path.exists(config_path):
            with open(config_path) as f:
                return json.load(f)
    except Exception as e:
        print(f"Warning: Failed to load llm_providers.json: {e}")

    # Fallback if file missing or error
    return {
        "embedding_models": {
            "nomic-embed-text": {
                "dimensions": 768,
                "provider": "ollama",
                "max_tokens": 8192,
            }
        },
        "vision_models": {"moondream": {"description": "Fallback Moondream"}},
        "default_embedding_model": "nomic-embed-text",
        "default_vision_model": "moondream",
    }


_RAW_CONFIG = _load_llm_config()

EMBEDDING_MODELS = _RAW_CONFIG.get("embedding_models", {})
DEFAULT_EMBEDDING_MODEL = _RAW_CONFIG.get("default_embedding_model", "nomic-embed-text")

VISION_MODELS = _RAW_CONFIG.get("vision_models", {})
DEFAULT_VISION_MODEL = _RAW_CONFIG.get("default_vision_model", "moondream")

# ─────────────────────────────────────────────────────────────────────────────
# Vector Search Configuration
# ─────────────────────────────────────────────────────────────────────────────

# Distance metrics supported by sqlite-vec
DISTANCE_METRICS = ("L2", "cosine", "L1")
DEFAULT_DISTANCE_METRIC = "cosine"

# Default search parameters
DEFAULT_TOP_K = 5
MAX_TOP_K = 100

# Chunking configuration
DEFAULT_CHUNK_SIZE = 1000  # characters
DEFAULT_CHUNK_OVERLAP = 200

# ─────────────────────────────────────────────────────────────────────────────
# RAG Configuration Dataclass
# ─────────────────────────────────────────────────────────────────────────────


@dataclass
class RAGConfig:
    """
    Configuration for MCard RAG system.

    Attributes:
        embedding_model: Name of the embedding model to use
        embedding_provider: Provider for embeddings (ollama, openai, etc.)
        ollama_base_url: Base URL for Ollama API
        vector_db_path: Path to vector database (None = use main MCard db)
        distance_metric: Distance metric for similarity search
        top_k: Default number of results to return
        chunk_size: Maximum characters per chunk
        chunk_overlap: Overlap between chunks
        enable_hybrid_search: Enable FTS + vector hybrid search
        rerank_results: Re-rank results using cross-encoder
    """

    # Embedding configuration
    embedding_model: str = DEFAULT_EMBEDDING_MODEL
    embedding_provider: str = "ollama"
    ollama_base_url: str = "http://localhost:11434"

    # Vector storage
    vector_db_path: str | None = None  # None = same as MCard db
    distance_metric: str = DEFAULT_DISTANCE_METRIC

    # Search parameters
    top_k: int = DEFAULT_TOP_K
    similarity_threshold: float = 0.0  # Minimum similarity (0-1)

    # Chunking
    chunk_size: int = DEFAULT_CHUNK_SIZE
    chunk_overlap: int = DEFAULT_CHUNK_OVERLAP

    # Advanced features
    enable_hybrid_search: bool = True
    rerank_results: bool = False

    def __post_init__(self):
        """Validate configuration after initialization."""
        if self.embedding_model not in EMBEDDING_MODELS:
            raise ValueError(
                f"Unknown embedding model: {self.embedding_model}. "
                f"Available: {list(EMBEDDING_MODELS.keys())}"
            )

        if self.distance_metric not in DISTANCE_METRICS:
            raise ValueError(
                f"Unknown distance metric: {self.distance_metric}. "
                f"Available: {DISTANCE_METRICS}"
            )

        if self.top_k < 1 or self.top_k > MAX_TOP_K:
            raise ValueError(f"top_k must be between 1 and {MAX_TOP_K}")

    @property
    def dimensions(self) -> int:
        """Get embedding dimensions for the configured model."""
        return EMBEDDING_MODELS[self.embedding_model]["dimensions"]

    @property
    def max_tokens(self) -> int:
        """Get max tokens for the configured model."""
        return EMBEDDING_MODELS[self.embedding_model]["max_tokens"]


# Default configuration
DEFAULT_RAG_CONFIG = RAGConfig()
