from datetime import datetime

from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, JSON, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.db.database import Base


class Repair(Base):
    __tablename__ = "repairs"

    id: Mapped[int] = mapped_column(primary_key=True)
    repair_number: Mapped[str] = mapped_column(String(20), unique=True, index=True)
    customer_id: Mapped[int | None] = mapped_column(ForeignKey("customers.id", ondelete="SET NULL"), nullable=True, index=True)
    contact_id: Mapped[int | None] = mapped_column(ForeignKey("customer_contacts.id", ondelete="SET NULL"), nullable=True, index=True)
    status: Mapped[str] = mapped_column(String(50), index=True)
    priority: Mapped[str] = mapped_column(String(30), index=True)
    source: Mapped[str] = mapped_column(String(50), index=True)
    source_reference: Mapped[str | None] = mapped_column(String(120), nullable=True, index=True)
    customer_name: Mapped[str] = mapped_column(String(255), index=True)
    customer_email: Mapped[str] = mapped_column(String(255), default="", server_default="")
    customer_phone: Mapped[str] = mapped_column(String(80), default="", server_default="")
    device_manufacturer: Mapped[str] = mapped_column(String(160), index=True)
    device_model: Mapped[str] = mapped_column(String(160), index=True)
    device_serial_number: Mapped[str | None] = mapped_column(String(160), nullable=True)
    device_type: Mapped[str] = mapped_column(String(120), default="", server_default="")
    accessories: Mapped[str] = mapped_column(Text, default="", server_default="")
    fault_description: Mapped[str] = mapped_column(Text)
    previous_work: Mapped[str] = mapped_column(Text, default="", server_default="")
    device_opened: Mapped[bool] = mapped_column(Boolean, default=False, server_default="false")
    intake_notes: Mapped[str] = mapped_column(Text, default="", server_default="")
    diagnosis_notes: Mapped[str] = mapped_column(Text, default="", server_default="")
    repair_notes: Mapped[str] = mapped_column(Text, default="", server_default="")
    estimate_notes: Mapped[str] = mapped_column(Text, default="", server_default="")
    estimated_cost_cents: Mapped[int | None] = mapped_column(Integer, nullable=True)
    approved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

    history: Mapped[list["RepairStatusHistory"]] = relationship(back_populates="repair", cascade="all, delete-orphan", lazy="selectin")
    public_access_tokens: Mapped[list["RepairPublicAccessToken"]] = relationship(back_populates="repair", cascade="all, delete-orphan")
    notification_events: Mapped[list["RepairNotificationEvent"]] = relationship(back_populates="repair", cascade="all, delete-orphan")


class RepairStatusHistory(Base):
    __tablename__ = "repair_status_history"

    id: Mapped[int] = mapped_column(primary_key=True)
    repair_id: Mapped[int] = mapped_column(ForeignKey("repairs.id", ondelete="CASCADE"), index=True)
    old_status: Mapped[str | None] = mapped_column(String(50), nullable=True)
    new_status: Mapped[str] = mapped_column(String(50), index=True)
    note: Mapped[str | None] = mapped_column(Text, nullable=True)
    actor_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

    repair: Mapped[Repair] = relationship(back_populates="history")
    actor = relationship("User", lazy="joined")

    @property
    def actor_username(self) -> str:
        return self.actor.username if self.actor is not None else ""

    @property
    def actor_display_name(self) -> str:
        if self.actor is None:
            return ""
        display_name = f"{self.actor.first_name} {self.actor.last_name}".strip()
        return display_name or self.actor.username


class RepairIntakeEvent(Base):
    __tablename__ = "repair_intake_events"

    id: Mapped[int] = mapped_column(primary_key=True)
    repair_id: Mapped[int | None] = mapped_column(ForeignKey("repairs.id", ondelete="SET NULL"), nullable=True, index=True)
    external_source: Mapped[str] = mapped_column(String(80), index=True)
    external_reference: Mapped[str | None] = mapped_column(String(160), nullable=True, index=True)
    payload: Mapped[dict] = mapped_column(JSON)
    status: Mapped[str] = mapped_column(String(50), index=True)
    error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    processed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)


class RepairDocument(Base):
    __tablename__ = "repair_documents"

    id: Mapped[int] = mapped_column(primary_key=True)
    repair_id: Mapped[int] = mapped_column(ForeignKey("repairs.id", ondelete="CASCADE"), index=True)
    file_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    storage_path: Mapped[str | None] = mapped_column(String(500), nullable=True)
    title: Mapped[str] = mapped_column(String(255))
    document_type: Mapped[str] = mapped_column(String(80), index=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())


class RepairPublicAccessToken(Base):
    __tablename__ = "repair_public_access_tokens"

    id: Mapped[int] = mapped_column(primary_key=True)
    repair_id: Mapped[int] = mapped_column(ForeignKey("repairs.id", ondelete="CASCADE"), index=True)
    token_hash: Mapped[str] = mapped_column(String(64), unique=True, index=True)
    token_hint: Mapped[str | None] = mapped_column(String(16), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, server_default="true", index=True)
    expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    last_used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    repair: Mapped[Repair] = relationship(back_populates="public_access_tokens")


class RepairNotificationEvent(Base):
    __tablename__ = "repair_notification_events"

    id: Mapped[int] = mapped_column(primary_key=True)
    repair_id: Mapped[int] = mapped_column(ForeignKey("repairs.id", ondelete="CASCADE"), index=True)
    event_type: Mapped[str] = mapped_column(String(80), index=True)
    channel: Mapped[str] = mapped_column(String(40), index=True)
    recipient: Mapped[str] = mapped_column(String(255), default="", server_default="")
    subject: Mapped[str] = mapped_column(String(255))
    template: Mapped[str] = mapped_column(Text, default="", server_default="")
    status: Mapped[str] = mapped_column(String(40), index=True)
    success: Mapped[bool] = mapped_column(Boolean, default=False, server_default="false", index=True)
    error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    sent_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    repair: Mapped[Repair] = relationship(back_populates="notification_events")
