import enum
from datetime import date, datetime

from sqlalchemy import Date, DateTime, ForeignKey, Index, Integer, Text
from sqlalchemy.dialects.postgresql import ARRAY, JSONB
from sqlalchemy.orm import Mapped, mapped_column

from fabledassistant.models import Base
from fabledassistant.models.base import TimestampMixin


class TaskStatus(str, enum.Enum):
    todo = "todo"
    in_progress = "in_progress"
    done = "done"
    cancelled = "cancelled"


class TaskPriority(str, enum.Enum):
    none = "none"
    low = "low"
    medium = "medium"
    high = "high"


class Note(Base, TimestampMixin):
    __tablename__ = "notes"

    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int | None] = mapped_column(
        Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True
    )
    title: Mapped[str] = mapped_column(Text, default="")
    body: Mapped[str] = mapped_column(Text, default="")
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    consolidated_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    tags: Mapped[list[str]] = mapped_column(ARRAY(Text), default=list)
    parent_id: Mapped[int | None] = mapped_column(
        Integer, ForeignKey("notes.id", ondelete="SET NULL"), nullable=True
    )
    project_id: Mapped[int | None] = mapped_column(
        Integer, ForeignKey("projects.id", ondelete="SET NULL"), nullable=True
    )
    milestone_id: Mapped[int | None] = mapped_column(
        Integer, ForeignKey("milestones.id", ondelete="SET NULL"), nullable=True
    )
    status: Mapped[str | None] = mapped_column(Text, nullable=True)
    priority: Mapped[str | None] = mapped_column(Text, nullable=True)
    due_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    recurrence_rule: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
    recurrence_next_spawn_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    # Entity type — 'note' (default), 'person', 'place', 'list'
    note_type: Mapped[str] = mapped_column(Text, default="note", server_default="note")
    # Structured metadata for entity types (person/place/list)
    # Named 'entity_meta' to avoid collision with SQLAlchemy's reserved 'metadata' attribute
    entity_meta: Mapped[dict | None] = mapped_column("metadata", JSONB, nullable=True)

    __table_args__ = (
        Index("ix_notes_tags", "tags", postgresql_using="gin"),
        Index("ix_notes_status", "status"),
        Index("ix_notes_title", "title"),
        Index("ix_notes_user_id", "user_id"),
        Index("ix_notes_project_id", "project_id"),
        Index("ix_notes_milestone_id", "milestone_id"),
        Index("ix_notes_note_type", "note_type"),
    )

    @property
    def is_task(self) -> bool:
        return self.status is not None

    @property
    def entity_type(self) -> str:
        """Normalised type: 'note', 'person', 'place', or 'list'."""
        return self.note_type or "note"

    def to_dict(self) -> dict:
        return {
            "id": self.id,
            "title": self.title,
            "body": self.body,
            "description": self.description,
            "consolidated_at": (
                self.consolidated_at.isoformat() if self.consolidated_at else None
            ),
            "tags": self.tags or [],
            "parent_id": self.parent_id,
            "project_id": self.project_id,
            "milestone_id": self.milestone_id,
            "status": self.status,
            "priority": self.priority,
            "due_date": self.due_date.isoformat() if self.due_date else None,
            "started_at": self.started_at.isoformat() if self.started_at else None,
            "completed_at": self.completed_at.isoformat() if self.completed_at else None,
            "recurrence_rule": self.recurrence_rule,
            "recurrence_next_spawn_at": (
                self.recurrence_next_spawn_at.isoformat()
                if self.recurrence_next_spawn_at
                else None
            ),
            "is_task": self.is_task,
            "note_type": self.entity_type,
            "metadata": self.entity_meta or {},
            "created_at": self.created_at.isoformat(),
            "updated_at": self.updated_at.isoformat(),
        }
