"""Semantic entities - cross-source business concepts (Level 2)."""

from enum import Enum

from pydantic import Field

from semantic_model.base import (
    NamedModel,
    SemanticBaseModel,
    SourceId,
    TableId,
    ColumnId,
    FullyQualifiedName,
    SqlExpression,
)
from semantic_model.columns import SemanticType


class ManifestationRole(str, Enum):
    """Role of a manifestation in representing an entity."""

    PRIMARY = "primary"
    SECONDARY = "secondary"
    DERIVED = "derived"


class AttributeSource(SemanticBaseModel):
    """A source for a unified attribute."""

    source_id: SourceId
    table_id: TableId
    column_id: ColumnId
    fully_qualified_column: str = Field(
        ...,
        description="catalog.schema.table.column",
    )
    transformation: SqlExpression | None = Field(default=None)
    priority: int = Field(default=1, ge=1, description="Lower = preferred source")
    freshness: str = Field(default="unknown")


class ResolutionStrategy(str, Enum):
    """Strategy for resolving attribute values from multiple sources."""

    PREFER_PRIMARY = "prefer_primary"
    MOST_RECENT = "most_recent"
    COALESCE = "coalesce"
    CUSTOM = "custom"


class UnifiedAttribute(NamedModel):
    """A logical attribute of an entity that may come from multiple sources."""

    semantic_type: SemanticType

    sources: list[AttributeSource] = Field(
        default_factory=list,
        description="Where this attribute can be sourced from, in priority order",
    )

    resolution_strategy: ResolutionStrategy = Field(
        default=ResolutionStrategy.PREFER_PRIMARY,
    )
    resolution_expression: SqlExpression | None = Field(
        default=None,
        description="SQL expression for custom resolution",
    )

    confidence: float = Field(default=0.5, ge=0.0, le=1.0)

    @property
    def primary_source(self) -> AttributeSource | None:
        """Get the highest priority source."""
        if not self.sources:
            return None
        return min(self.sources, key=lambda s: s.priority)


class EntityManifestation(SemanticBaseModel):
    """Where a semantic entity physically exists in a data source."""

    # Location
    source_id: SourceId
    table_id: TableId
    fully_qualified_name: FullyQualifiedName

    # Role
    role: ManifestationRole = Field(default=ManifestationRole.SECONDARY)

    # Identity
    key_column_id: ColumnId = Field(
        ...,
        description="Column that identifies the entity in this table",
    )
    key_transformation: SqlExpression | None = Field(
        default=None,
        description="SQL expression to transform the key to its canonical form",
    )

    # Freshness
    freshness_notes: str = Field(
        default="",
        description="How fresh is data in this manifestation",
    )

    # Usage guidance
    usage_guidance: str = Field(
        default="",
        description="When to use this manifestation",
    )

    # Confidence
    confidence: float = Field(default=0.5, ge=0.0, le=1.0)

    @property
    def is_primary(self) -> bool:
        """Check if this is the primary manifestation."""
        return self.role == ManifestationRole.PRIMARY


class EntityStatus(str, Enum):
    """Status of a semantic entity."""

    DRAFT = "draft"
    STAGING = "staging"
    CONFIRMED = "confirmed"
    DEPRECATED = "deprecated"


class SemanticEntity(NamedModel):
    """A canonical business concept that spans multiple data sources."""

    # Business context
    business_context: str = Field(
        default="",
        description="Role of this entity in the organization",
    )

    # Domain
    domain: str = Field(
        default="",
        description="Primary business domain",
    )

    # Canonical identifier
    canonical_id_name: str = Field(
        ...,
        description="Name of the canonical ID: 'customer_id'",
    )
    canonical_id_format: str = Field(
        default="",
        description="Format of the ID: 'UUID', 'CUST-NNNNNN'",
    )
    canonical_id_description: str = Field(default="")

    # Manifestations
    manifestations: list[EntityManifestation] = Field(default_factory=list)

    # Unified attributes
    unified_attributes: list[UnifiedAttribute] = Field(default_factory=list)

    # Cross-source joining
    cross_source_join_hint: str = Field(
        default="",
        description="How to join this entity across sources (SQL or natural language)",
    )
    cross_source_confidence: float = Field(default=0.5, ge=0.0, le=1.0)

    # Active record filter
    active_filter: SqlExpression | None = Field(
        default=None,
        description="SQL condition to filter active (non-deleted) records",
    )

    # Status
    status: EntityStatus = Field(default=EntityStatus.DRAFT)

    # Common queries
    common_queries: list[str] = Field(
        default_factory=list,
        description="Common questions about this entity",
    )

    @property
    def primary_manifestation(self) -> EntityManifestation | None:
        """Get the primary manifestation of this entity."""
        for m in self.manifestations:
            if m.is_primary:
                return m
        return None

    @property
    def source_ids(self) -> list[SourceId]:
        """Get all source IDs where this entity appears."""
        return list(set(m.source_id for m in self.manifestations))

    def get_manifestation(self, source_id: SourceId) -> EntityManifestation | None:
        """Get manifestation for a specific source."""
        for m in self.manifestations:
            if m.source_id == source_id:
                return m
        return None

    def get_attribute(self, name: str) -> UnifiedAttribute | None:
        """Get a unified attribute by name."""
        for attr in self.unified_attributes:
            if attr.name == name:
                return attr
        return None

    def to_prompt_format(self) -> str:
        """Convert to a compact format for LLM prompts."""
        lines = [
            f"# Entity: {self.name}",
            f"Canonical ID: {self.canonical_id_name}",
        ]

        if self.description:
            lines.append(f"Description: {self.description}")

        primary = self.primary_manifestation
        if primary:
            lines.append(f"Primary source: {primary.fully_qualified_name}")

        others = [m for m in self.manifestations if not m.is_primary]
        if others:
            lines.append("Also found in:")
            for m in others:
                lines.append(f"  - {m.fully_qualified_name} ({m.role.value})")

        if self.cross_source_join_hint:
            lines.append(f"Cross-source join: {self.cross_source_join_hint}")

        if self.active_filter:
            lines.append(f"Active filter: {self.active_filter}")

        if self.unified_attributes:
            lines.append("Key attributes:")
            for attr in self.unified_attributes[:10]:
                lines.append(f"  - {attr.name}: {attr.description}")

        return "\n".join(lines)
