# app/models/memory/procedure.py
"""
Procedure database model.

Stores procedural memories - learned skills and procedures
derived from successful task execution patterns.
"""

from sqlalchemy import (
    CheckConstraint,
    Column,
    DateTime,
    ForeignKey,
    Index,
    Integer,
    String,
    Text,
)
from sqlalchemy.dialects.postgresql import (
    JSONB,
    UUID as PGUUID,
)
from sqlalchemy.orm import relationship

from app.models.base import Base, TimestampMixin, UUIDMixin

# Import pgvector type
try:
    from pgvector.sqlalchemy import Vector  # type: ignore[import-not-found]
except ImportError:
    Vector = None


class Procedure(Base, UUIDMixin, TimestampMixin):
    """
    Procedural memory model.

    Stores learned procedures (skills) extracted from successful
    task execution patterns:
    - Name and trigger pattern for matching
    - Step-by-step actions
    - Success/failure tracking
    """

    __tablename__ = "procedures"

    # Scoping
    project_id = Column(
        PGUUID(as_uuid=True),
        ForeignKey("projects.id", ondelete="CASCADE"),
        nullable=True,
        index=True,
    )

    agent_type_id = Column(
        PGUUID(as_uuid=True),
        ForeignKey("agent_types.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
    )

    # Procedure identification
    name = Column(String(255), nullable=False, index=True)
    trigger_pattern = Column(Text, nullable=False)

    # Steps as JSON array of step objects
    # Each step: {order, action, parameters, expected_outcome, fallback_action}
    steps = Column(JSONB, default=list, nullable=False)

    # Success tracking
    success_count = Column(Integer, nullable=False, default=0)
    failure_count = Column(Integer, nullable=False, default=0)

    # Usage tracking
    last_used = Column(DateTime(timezone=True), nullable=True, index=True)

    # Vector embedding for semantic matching
    embedding = Column(Vector(1536) if Vector else Text, nullable=True)

    # Relationships
    project = relationship("Project", foreign_keys=[project_id])
    agent_type = relationship("AgentType", foreign_keys=[agent_type_id])

    __table_args__ = (
        # Unique procedure name within scope
        Index(
            "ix_procedures_unique_name",
            "project_id",
            "agent_type_id",
            "name",
            unique=True,
        ),
        # Query patterns
        Index("ix_procedures_project_name", "project_id", "name"),
        # Note: agent_type_id already has index=True on Column definition
        # For finding best procedures
        Index("ix_procedures_success_rate", "success_count", "failure_count"),
        # Data integrity constraints
        CheckConstraint(
            "success_count >= 0",
            name="ck_procedures_success_positive",
        ),
        CheckConstraint(
            "failure_count >= 0",
            name="ck_procedures_failure_positive",
        ),
    )

    @property
    def success_rate(self) -> float:
        """Calculate the success rate of this procedure."""
        # Snapshot values to avoid race conditions in concurrent access
        success = self.success_count
        failure = self.failure_count
        total = success + failure
        if total == 0:
            return 0.0
        return success / total

    @property
    def total_uses(self) -> int:
        """Get total number of times this procedure was used."""
        # Snapshot values for consistency
        return self.success_count + self.failure_count

    def __repr__(self) -> str:
        return (
            f"<Procedure {self.name} ({self.id}) success_rate={self.success_rate:.2%}>"
        )
