import enum
from datetime import datetime

from sqlalchemy import BigInteger, Boolean, DateTime, ForeignKey, Integer, String, Text
from sqlalchemy import Enum as SAEnum
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import JSON

from app.models.base import BaseModel


class TicketStatus(str, enum.Enum):
    OPEN = "open"
    PENDING = "pending"
    PROCESSING = "processing"
    WAITING_USER = "waiting_user"
    ANSWERED = "answered"
    RESOLVED = "resolved"
    CLOSED = "closed"


class TicketPriority(str, enum.Enum):
    LOW = "low"
    NORMAL = "normal"
    HIGH = "high"
    URGENT = "urgent"


class TicketSenderType(str, enum.Enum):
    USER = "user"
    ADMIN = "admin"
    SYSTEM = "system"


class TicketEventType(str, enum.Enum):
    CREATED = "created"
    USER_REPLIED = "user_replied"
    ADMIN_REPLIED = "admin_replied"
    INTERNAL_NOTE_ADDED = "internal_note_added"
    ASSIGNED = "assigned"
    STATUS_CHANGED = "status_changed"
    PRIORITY_CHANGED = "priority_changed"
    DEPARTMENT_CHANGED = "department_changed"
    CLOSED = "closed"
    REOPENED = "reopened"
    RATED = "rated"


class TicketDepartment(BaseModel):
    __tablename__ = "ticket_departments"

    code: Mapped[str | None] = mapped_column(String(64), unique=True, nullable=True, index=True)
    name: Mapped[str] = mapped_column(String(80), nullable=False)
    description: Mapped[str | None] = mapped_column(String(255), nullable=True)
    is_enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    min_member_level: Mapped[int | None] = mapped_column(Integer, nullable=True)
    auto_reply_enabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    auto_reply_content: Mapped[str | None] = mapped_column(Text, nullable=True)


class Ticket(BaseModel):
    __tablename__ = "tickets"

    ticket_no: Mapped[str] = mapped_column(String(32), unique=True, nullable=False, index=True)
    user_id: Mapped[int] = mapped_column(
        BigInteger, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
    )
    department_id: Mapped[int | None] = mapped_column(
        ForeignKey("ticket_departments.id", ondelete="SET NULL"), nullable=True, index=True
    )
    department_name: Mapped[str | None] = mapped_column(String(80), nullable=True)
    instance_id: Mapped[int | None] = mapped_column(
        ForeignKey("instances.id", ondelete="SET NULL"), nullable=True, index=True
    )
    subject: Mapped[str] = mapped_column(String(160), nullable=False)
    priority: Mapped[TicketPriority] = mapped_column(
        SAEnum(TicketPriority, values_callable=lambda e: [m.value for m in e], native_enum=False, length=32),
        default=TicketPriority.NORMAL,
        nullable=False,
        index=True,
    )
    status: Mapped[TicketStatus] = mapped_column(
        SAEnum(TicketStatus, values_callable=lambda e: [m.value for m in e], native_enum=False, length=32),
        default=TicketStatus.OPEN,
        nullable=False,
        index=True,
    )
    last_message_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
    last_reply_by: Mapped[TicketSenderType | None] = mapped_column(
        SAEnum(TicketSenderType, values_callable=lambda e: [m.value for m in e]),
        nullable=True,
    )
    assignee_id: Mapped[int | None] = mapped_column(
        BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True
    )
    assigned_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    user_unread_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    admin_unread_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    closed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    satisfaction_rating: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True)
    satisfaction_comment: Mapped[str | None] = mapped_column(Text, nullable=True)
    satisfaction_rated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)


class TicketMessage(BaseModel):
    __tablename__ = "ticket_messages"

    ticket_id: Mapped[int] = mapped_column(
        ForeignKey("tickets.id", ondelete="CASCADE"), nullable=False, index=True
    )
    sender_type: Mapped[TicketSenderType] = mapped_column(
        SAEnum(TicketSenderType, values_callable=lambda e: [m.value for m in e]),
        nullable=False,
        index=True,
    )
    sender_id: Mapped[int | None] = mapped_column(
        BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True
    )
    content: Mapped[str] = mapped_column(Text, nullable=False)
    attachments: Mapped[list | None] = mapped_column(JSON, nullable=True)
    is_internal: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)


class TicketEvent(BaseModel):
    __tablename__ = "ticket_events"

    ticket_id: Mapped[int] = mapped_column(
        ForeignKey("tickets.id", ondelete="CASCADE"), nullable=False, index=True
    )
    actor_type: Mapped[TicketSenderType] = mapped_column(
        SAEnum(TicketSenderType, values_callable=lambda e: [m.value for m in e], native_enum=False, length=32),
        nullable=False,
        index=True,
    )
    actor_id: Mapped[int | None] = mapped_column(
        BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True
    )
    event_type: Mapped[TicketEventType] = mapped_column(
        SAEnum(TicketEventType, values_callable=lambda e: [m.value for m in e], native_enum=False, length=64),
        nullable=False,
        index=True,
    )
    from_value: Mapped[str | None] = mapped_column(String(160), nullable=True)
    to_value: Mapped[str | None] = mapped_column(String(160), nullable=True)
    message: Mapped[str | None] = mapped_column(Text, nullable=True)
    meta: Mapped[dict | None] = mapped_column(JSON, nullable=True)


class TicketAttachment(BaseModel):
    __tablename__ = "ticket_attachments"

    token: Mapped[str] = mapped_column(String(80), unique=True, nullable=False, index=True)
    storage_path: Mapped[str] = mapped_column(String(500), nullable=False)
    original_name: Mapped[str | None] = mapped_column(String(160), nullable=True)
    content_type: Mapped[str] = mapped_column(String(100), nullable=False)
    size: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    uploaded_by_type: Mapped[TicketSenderType] = mapped_column(
        SAEnum(TicketSenderType, values_callable=lambda e: [m.value for m in e], native_enum=False, length=32),
        nullable=False,
        index=True,
    )
    uploaded_by_id: Mapped[int | None] = mapped_column(
        BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True
    )
    ticket_id: Mapped[int | None] = mapped_column(
        ForeignKey("tickets.id", ondelete="CASCADE"), nullable=True, index=True
    )
    message_id: Mapped[int | None] = mapped_column(
        ForeignKey("ticket_messages.id", ondelete="SET NULL"), nullable=True, index=True
    )
    is_claimed: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True)


class TicketReplyTemplate(BaseModel):
    __tablename__ = "ticket_reply_templates"

    title: Mapped[str] = mapped_column(String(120), nullable=False)
    content: Mapped[str] = mapped_column(Text, nullable=False)
    department_id: Mapped[int | None] = mapped_column(
        ForeignKey("ticket_departments.id", ondelete="SET NULL"), nullable=True, index=True
    )
    is_enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    usage_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    created_by_id: Mapped[int | None] = mapped_column(
        BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True
    )
    updated_by_id: Mapped[int | None] = mapped_column(
        BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True
    )
