"""Expert overrides and feedback models."""

from datetime import datetime
from enum import Enum
from typing import Any

from pydantic import Field

from semantic_model.base import SemanticBaseModel, JsonPath, SourceId


class OverrideStatus(str, Enum):
    """Status of an expert override."""

    ACTIVE = "active"
    SUPERSEDED = "superseded"
    REVERTED = "reverted"


class ReindexScope(str, Enum):
    """Scope of reindexing triggered by an override."""

    THIS_OBJECT = "this_object"
    THIS_SOURCE = "this_source"
    DEPENDENT_SOURCES = "dependent_sources"
    FULL = "full"


class ExpertOverride(SemanticBaseModel):
    """An override made by a domain expert to correct or enhance agent output."""

    id: str = Field(..., description="Unique override ID")
    created_at: datetime = Field(default_factory=datetime.utcnow)
    created_by: str = Field(..., description="Expert identifier")

    # What was overridden
    field_path: JsonPath = Field(
        ...,
        description="JSON path to field: 'description', 'semantic_type.category'",
    )
    original_value: Any = Field(..., description="Value before override")
    override_value: Any = Field(..., description="New value set by expert")

    # Context
    reason: str = Field(..., description="Why the expert made this change")
    additional_context: str = Field(
        default="",
        description="Extra context for the reindexing agent",
    )

    # Impact
    should_trigger_reindex: bool = Field(
        default=True,
        description="Whether this override should trigger reindexing",
    )
    reindex_scope: ReindexScope = Field(
        default=ReindexScope.THIS_OBJECT,
        description="Scope of reindexing if triggered",
    )

    # Status
    status: OverrideStatus = Field(default=OverrideStatus.ACTIVE)
    superseded_by: str | None = Field(
        default=None,
        description="ID of override that replaced this one",
    )

    def supersede(self, new_override_id: str) -> "ExpertOverride":
        """Mark this override as superseded by a new one."""
        return self.model_copy(
            update={
                "status": OverrideStatus.SUPERSEDED,
                "superseded_by": new_override_id,
            }
        )

    def revert(self) -> "ExpertOverride":
        """Mark this override as reverted."""
        return self.model_copy(update={"status": OverrideStatus.REVERTED})


class FeedbackType(str, Enum):
    """Types of feedback an expert can provide."""

    CORRECTION = "correction"
    CLARIFICATION = "clarification"
    ENHANCEMENT = "enhancement"
    APPROVAL = "approval"


class FeedbackPriority(str, Enum):
    """Priority of a feedback item."""

    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"


class FeedbackObjectType(str, Enum):
    """Types of objects feedback can be about."""

    SOURCE = "source"
    TABLE = "table"
    COLUMN = "column"
    RELATIONSHIP = "relationship"
    ENTITY = "entity"


class FeedbackItem(SemanticBaseModel):
    """A single item of feedback from a domain expert."""

    object_type: FeedbackObjectType
    object_id: str = Field(..., description="ID of the object")
    feedback_type: FeedbackType
    field_path: JsonPath = Field(..., description="Path to the field")
    current_value: Any = Field(..., description="Current agent-generated value")
    suggested_value: Any = Field(..., description="Expert's suggested value")
    explanation: str = Field(..., description="Why this change is needed")
    priority: FeedbackPriority = Field(default=FeedbackPriority.MEDIUM)


class FeedbackAssessment(str, Enum):
    """Overall assessment of a source by an expert."""

    APPROVED = "approved"
    NEEDS_WORK = "needs_work"
    REJECTED = "rejected"


class ExpertFeedback(SemanticBaseModel):
    """Structured feedback from a domain expert during review."""

    id: str = Field(..., description="Unique feedback ID")
    source_id: SourceId = Field(..., description="Source being reviewed")
    created_at: datetime = Field(default_factory=datetime.utcnow)
    expert_id: str = Field(..., description="Expert who provided feedback")

    # Feedback items
    items: list[FeedbackItem] = Field(default_factory=list)

    # Overall assessment
    overall_assessment: FeedbackAssessment
    general_notes: str = Field(default="", description="General notes about the source")

    # Processing status
    processed: bool = Field(default=False, description="Whether feedback has been processed")
    reindex_job_id: str | None = Field(
        default=None,
        description="ID of reindex job triggered by this feedback",
    )
    processed_at: datetime | None = Field(default=None)

    def mark_processed(self, reindex_job_id: str) -> "ExpertFeedback":
        """Mark feedback as processed."""
        return self.model_copy(
            update={
                "processed": True,
                "reindex_job_id": reindex_job_id,
                "processed_at": datetime.utcnow(),
            }
        )

    @property
    def critical_items(self) -> list[FeedbackItem]:
        """Get critical priority items."""
        return [i for i in self.items if i.priority == FeedbackPriority.CRITICAL]

    @property
    def correction_count(self) -> int:
        """Count of correction items."""
        return len([i for i in self.items if i.feedback_type == FeedbackType.CORRECTION])


class OverrideManager:
    """Utility class for managing expert overrides on an object."""

    def __init__(self, overrides: list[ExpertOverride] | None = None):
        self.overrides = overrides or []

    def add_override(self, override: ExpertOverride) -> list[ExpertOverride]:
        """Add a new override, superseding any existing override for the same field."""
        result = []
        for existing in self.overrides:
            if (
                existing.field_path == override.field_path
                and existing.status == OverrideStatus.ACTIVE
            ):
                result.append(existing.supersede(override.id))
            else:
                result.append(existing)
        result.append(override)
        self.overrides = result
        return result

    def get_active_overrides(self) -> list[ExpertOverride]:
        """Get all active (non-superseded, non-reverted) overrides."""
        return [o for o in self.overrides if o.status == OverrideStatus.ACTIVE]

    def get_override_for_field(self, field_path: JsonPath) -> ExpertOverride | None:
        """Get the active override for a specific field, if any."""
        for override in self.overrides:
            if override.field_path == field_path and override.status == OverrideStatus.ACTIVE:
                return override
        return None

    def apply_overrides(self, data: dict[str, Any]) -> dict[str, Any]:
        """Apply all active overrides to a data dictionary.
        
        This is a simple implementation that handles single-level paths.
        For nested paths, you'd need more sophisticated logic.
        """
        result = data.copy()
        for override in self.get_active_overrides():
            # Simple single-level path handling
            if "." not in override.field_path:
                result[override.field_path] = override.override_value
            else:
                # For nested paths like "semantic_type.category"
                parts = override.field_path.split(".")
                current = result
                for part in parts[:-1]:
                    if part not in current:
                        current[part] = {}
                    current = current[part]
                current[parts[-1]] = override.override_value
        return result
