"""Glossary terms for business terminology."""

from enum import Enum

from pydantic import Field

from semantic_model.base import (
    NamedModel,
    SourceId,
    EntityId,
    ColumnId,
)
from semantic_model.overrides import ExpertOverride


class GlossaryScope(str, Enum):
    """Scope of a glossary term."""

    GLOBAL = "global"
    DOMAIN = "domain"
    SOURCE = "source"


class GlossaryCreator(str, Enum):
    """Who created the glossary term."""

    LEVEL_1_AGENT = "level_1_agent"
    LEVEL_2_AGENT = "level_2_agent"
    EXPERT = "expert"


class GlossaryTerm(NamedModel):
    """A business term with its definition."""

    # The term
    term: str = Field(..., description="The word or phrase")
    definition: str = Field(..., description="Definition of the term")

    # Scope
    scope: GlossaryScope = Field(default=GlossaryScope.GLOBAL)
    domain: str | None = Field(
        default=None,
        description="Domain if scope is DOMAIN",
    )
    source_id: SourceId | None = Field(
        default=None,
        description="Source if scope is SOURCE",
    )

    # Related objects
    related_entities: list[EntityId] = Field(
        default_factory=list,
        description="Entities this term relates to",
    )
    related_columns: list[ColumnId] = Field(
        default_factory=list,
        description="Columns this term relates to",
    )

    # Synonyms and related terms
    synonyms: list[str] = Field(
        default_factory=list,
        description="Alternative names for this term",
    )
    related_terms: list[str] = Field(
        default_factory=list,
        description="Related but different terms",
    )

    # Abbreviations
    abbreviation: str | None = Field(
        default=None,
        description="Common abbreviation (e.g., 'LTV' for 'Lifetime Value')",
    )

    # Origin
    created_by: GlossaryCreator = Field(default=GlossaryCreator.LEVEL_1_AGENT)
    confidence: float = Field(default=0.5, ge=0.0, le=1.0)

    # Expert overrides
    expert_overrides: list[ExpertOverride] = Field(default_factory=list)

    @property
    def is_expert_verified(self) -> bool:
        """Check if this term has been verified by an expert."""
        return (
            self.created_by == GlossaryCreator.EXPERT
            or len(self.expert_overrides) > 0
        )

    def matches(self, query: str) -> bool:
        """Check if this term matches a query string.
        
        Args:
            query: Search query (case-insensitive)
            
        Returns:
            True if the term, any synonym, or abbreviation matches
        """
        query_lower = query.lower()
        
        if self.term.lower() == query_lower:
            return True
        
        if self.abbreviation and self.abbreviation.lower() == query_lower:
            return True
        
        if any(s.lower() == query_lower for s in self.synonyms):
            return True
        
        return False

    def to_prompt_format(self) -> str:
        """Convert to a compact format for LLM prompts."""
        parts = [f"**{self.term}**"]
        
        if self.abbreviation:
            parts[0] += f" ({self.abbreviation})"
        
        parts.append(f": {self.definition}")
        
        if self.synonyms:
            parts.append(f" Also known as: {', '.join(self.synonyms)}.")
        
        return "".join(parts)


class Glossary:
    """A collection of glossary terms with lookup functionality."""

    def __init__(self, terms: list[GlossaryTerm] | None = None):
        self.terms = terms or []
        self._build_indices()

    def _build_indices(self) -> None:
        """Build indices for fast lookup."""
        self._by_term: dict[str, GlossaryTerm] = {}
        self._by_abbreviation: dict[str, GlossaryTerm] = {}
        self._by_domain: dict[str, list[GlossaryTerm]] = {}
        self._by_source: dict[str, list[GlossaryTerm]] = {}
        
        for term in self.terms:
            self._by_term[term.term.lower()] = term
            
            if term.abbreviation:
                self._by_abbreviation[term.abbreviation.lower()] = term
            
            for synonym in term.synonyms:
                self._by_term[synonym.lower()] = term
            
            if term.domain:
                if term.domain not in self._by_domain:
                    self._by_domain[term.domain] = []
                self._by_domain[term.domain].append(term)
            
            if term.source_id:
                if term.source_id not in self._by_source:
                    self._by_source[term.source_id] = []
                self._by_source[term.source_id].append(term)

    def lookup(self, query: str) -> GlossaryTerm | None:
        """Look up a term by name, synonym, or abbreviation."""
        query_lower = query.lower()
        
        if query_lower in self._by_term:
            return self._by_term[query_lower]
        
        if query_lower in self._by_abbreviation:
            return self._by_abbreviation[query_lower]
        
        return None

    def search(self, query: str) -> list[GlossaryTerm]:
        """Search for terms containing the query string."""
        query_lower = query.lower()
        results = []
        
        for term in self.terms:
            if query_lower in term.term.lower():
                results.append(term)
            elif query_lower in term.definition.lower():
                results.append(term)
            elif any(query_lower in s.lower() for s in term.synonyms):
                results.append(term)
        
        return results

    def get_domain_terms(self, domain: str) -> list[GlossaryTerm]:
        """Get all terms for a specific domain."""
        return self._by_domain.get(domain, [])

    def get_source_terms(self, source_id: SourceId) -> list[GlossaryTerm]:
        """Get all terms for a specific data source."""
        return self._by_source.get(source_id, [])

    def get_global_terms(self) -> list[GlossaryTerm]:
        """Get all global-scope terms."""
        return [t for t in self.terms if t.scope == GlossaryScope.GLOBAL]

    def add_term(self, term: GlossaryTerm) -> None:
        """Add a new term and update indices."""
        self.terms.append(term)
        self._build_indices()

    def to_prompt_format(self, max_terms: int = 50) -> str:
        """Convert glossary to a compact format for LLM prompts."""
        lines = ["## Glossary\n"]
        
        for term in self.terms[:max_terms]:
            lines.append(term.to_prompt_format())
        
        if len(self.terms) > max_terms:
            lines.append(f"\n... and {len(self.terms) - max_terms} more terms")
        
        return "\n".join(lines)
