"""Core type definitions for the Ontology Engine.

6 Entity Types (Phase 1): Person, Decision, ActionItem, Project, Risk, Deadline
13 Link Types: participates_in, makes, assigned_to, relates_to, generates,
               supersedes, depends_on, mitigates, blocks, reports_to,
               collaborates_with, owns, deadline_for
"""

from __future__ import annotations

import enum
from datetime import date, datetime
from typing import Any

from pydantic import BaseModel, Field, field_validator, model_validator


# =============================================================================
# Enums
# =============================================================================


class EntityType(str, enum.Enum):
    """Core entity types (Phase 1)."""

    PERSON = "Person"
    DECISION = "Decision"
    ACTION_ITEM = "ActionItem"
    PROJECT = "Project"
    RISK = "Risk"
    DEADLINE = "Deadline"


class LinkType(str, enum.Enum):
    """Relationship types between entities."""

    PARTICIPATES_IN = "participates_in"  # Person → Project
    MAKES = "makes"  # Person → Decision
    ASSIGNED_TO = "assigned_to"  # ActionItem → Person
    RELATES_TO = "relates_to"  # Decision → Project
    GENERATES = "generates"  # Decision → ActionItem
    SUPERSEDES = "supersedes"  # Decision → Decision
    DEPENDS_ON = "depends_on"  # ActionItem → ActionItem
    MITIGATES = "mitigates"  # ActionItem → Risk
    BLOCKS = "blocks"  # Risk → Project
    REPORTS_TO = "reports_to"  # Person → Person
    COLLABORATES_WITH = "collaborates_with"  # Person → Person
    OWNS = "owns"  # Person → Project
    DEADLINE_FOR = "deadline_for"  # Deadline → any


class DecisionType(str, enum.Enum):
    STRATEGIC = "strategic"
    TACTICAL = "tactical"
    OPERATIONAL = "operational"


class EntityStatus(str, enum.Enum):
    ACTIVE = "active"
    ARCHIVED = "archived"
    DELETED = "deleted"


class ActionItemStatus(str, enum.Enum):
    PENDING = "pending"
    IN_PROGRESS = "in_progress"
    DONE = "done"
    CANCELLED = "cancelled"
    OVERDUE = "overdue"


class DecisionStatus(str, enum.Enum):
    PROPOSED = "proposed"
    ACTIVE = "active"
    SUPERSEDED = "superseded"
    REVOKED = "revoked"


class ProjectStatus(str, enum.Enum):
    PLANNING = "planning"
    ACTIVE = "active"
    PAUSED = "paused"
    COMPLETED = "completed"
    CANCELLED = "cancelled"


class RiskImpact(str, enum.Enum):
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"


class RiskCategory(str, enum.Enum):
    TECHNICAL = "technical"
    OPERATIONAL = "operational"
    MARKET = "market"
    LEGAL = "legal"
    PERSONNEL = "personnel"


# =============================================================================
# Core Models
# =============================================================================


class Entity(BaseModel):
    """A node in the ontology graph.

    Stores both fixed fields (id, type, name) and dynamic properties (JSONB).
    entity_type accepts EntityType enum (backward compat) or plain str (schema mode).
    """

    id: str | None = None  # Generated by DB: ENT-<uuid>
    entity_type: EntityType | str
    name: str
    properties: dict[str, Any] = Field(default_factory=dict)
    aliases: list[str] = Field(default_factory=list)
    status: EntityStatus = EntityStatus.ACTIVE
    confidence: float = Field(default=1.0, ge=0.0, le=1.0)
    version: int = 1

    # Metadata — filled by storage layer
    created_at: datetime | None = None
    updated_at: datetime | None = None
    created_by: str = "system"


class Link(BaseModel):
    """An edge in the ontology graph — a typed relationship between two entities.

    link_type accepts LinkType enum (backward compat) or plain str (schema mode).
    """

    id: str | None = None  # Generated by DB: LNK-<uuid>
    link_type: LinkType | str
    source_entity_id: str
    target_entity_id: str
    properties: dict[str, Any] = Field(default_factory=dict)
    confidence: float = Field(default=1.0, ge=0.0, le=1.0)
    status: EntityStatus = EntityStatus.ACTIVE

    # Temporal validity
    valid_from: datetime | None = None
    valid_to: datetime | None = None

    created_at: datetime | None = None
    created_by: str = "system"

    @field_validator("target_entity_id")
    @classmethod
    def no_self_link(cls, v: str, info: Any) -> str:
        source = info.data.get("source_entity_id")
        if source and v == source:
            raise ValueError("Self-referencing links are not allowed")
        return v


class Provenance(BaseModel):
    """Tracks where a piece of knowledge came from."""

    id: str | None = None
    entity_id: str | None = None
    link_id: str | None = None

    # Bronze layer reference (optional, backward-compatible)
    source_document_id: str | None = None  # References bronze_documents.id

    # Source info
    source_type: str  # meeting_transcript, manual_input, llm_extraction, etc.
    source_file: str | None = None
    source_meeting_date: date | None = None
    source_participants: list[str] = Field(default_factory=list)
    source_segment: str | None = None  # Original text excerpt

    # Extraction info
    extraction_model: str | None = None
    extraction_schema: str | None = None  # Domain schema name (e.g. "default", "edtech")
    extraction_pass: str | None = None  # pass1, pass2, pass3, pass4
    raw_extraction: dict[str, Any] | None = None

    created_at: datetime | None = None
    created_by: str = "system"

    @model_validator(mode="after")
    def must_have_target(self) -> Provenance:
        if self.entity_id is None and self.link_id is None:
            raise ValueError("Provenance must reference an entity_id or link_id")
        return self


# =============================================================================
# Pipeline Models
# =============================================================================


class ExtractedEntity(BaseModel):
    """Raw entity extracted by LLM before validation.

    entity_type accepts both EntityType enum (backward compat) and plain str
    (schema-driven mode where types are defined in YAML).
    """

    name: str
    entity_type: EntityType | str
    aliases: list[str] = Field(default_factory=list)
    confidence: float = Field(default=0.8, ge=0.0, le=1.0)
    is_new: bool = False
    context: str = ""  # Source text excerpt
    properties: dict[str, Any] = Field(default_factory=dict)


class ExtractedLink(BaseModel):
    """Raw relationship extracted by LLM before validation.

    link_type accepts both LinkType enum (backward compat) and plain str
    (schema-driven mode where types are defined in YAML).
    """

    link_type: LinkType | str
    source_name: str  # Resolved to entity_id during fusion
    target_name: str
    confidence: float = Field(default=0.8, ge=0.0, le=1.0)
    context: str = ""
    properties: dict[str, Any] = Field(default_factory=dict)


class ExtractedDecision(BaseModel):
    """A decision extracted from meeting text."""

    summary: str
    detail: str = ""
    decision_type: DecisionType = DecisionType.OPERATIONAL
    made_by: str = ""  # Person name
    participants: list[str] = Field(default_factory=list)
    rationale: str = ""
    supersedes: str | None = None  # ID of superseded decision
    conditions: str = ""
    confidence: float = Field(default=0.8, ge=0.0, le=1.0)
    source_segment: str = ""


class ExtractedActionItem(BaseModel):
    """An action item extracted from meeting text."""

    task: str
    owner: str = ""  # Person name
    assignees: list[str] = Field(default_factory=list)
    due_date: date | None = None
    priority: str = "medium"
    related_decision: str | None = None
    completion_criteria: str = ""
    confidence: float = Field(default=0.8, ge=0.0, le=1.0)
    source_segment: str = ""


class ExtractionResult(BaseModel):
    """Complete output of the 4-pass extraction pipeline."""

    entities: list[ExtractedEntity] = Field(default_factory=list)
    links: list[ExtractedLink] = Field(default_factory=list)
    decisions: list[ExtractedDecision] = Field(default_factory=list)
    action_items: list[ExtractedActionItem] = Field(default_factory=list)

    # Metadata
    source_file: str = ""
    meeting_date: date | None = None
    participants: list[str] = Field(default_factory=list)
    extraction_model: str = ""
    extraction_schema: str = ""  # Domain schema name (e.g. "default", "edtech")
    processing_time_ms: int = 0


# =============================================================================
# Validation Models
# =============================================================================


class ValidationError(BaseModel):
    """A single validation issue found during auto-correction."""

    layer: str  # "factual", "semantic", "consistency"
    severity: str  # "error", "warning", "info"
    entity_name: str | None = None
    field: str | None = None
    message: str
    suggestion: str | None = None
    auto_fixed: bool = False


class ValidationResult(BaseModel):
    """Output of the 3-layer validation pipeline."""

    is_valid: bool
    errors: list[ValidationError] = Field(default_factory=list)
    warnings: list[ValidationError] = Field(default_factory=list)
    auto_fixes_applied: int = 0
    extraction: ExtractionResult | None = None  # Post-correction result
