import enum
import uuid

from sqlalchemy import BigInteger, Boolean, Enum, ForeignKey, String, Text
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.db.session import Base
from app.models.base import TimestampMixin, UUIDPKMixin


class FileType(str, enum.Enum):
    pdf = "pdf"
    docx = "docx"
    txt = "txt"
    markdown = "markdown"
    html = "html"
    csv = "csv"
    json = "json"
    xml = "xml"


class SourceStatus(str, enum.Enum):
    uploaded = "uploaded"
    extracting = "extracting"
    extracted = "extracted"
    cleaning = "cleaning"
    cleaned = "cleaned"
    failed = "failed"


class KnowledgeSource(Base, UUIDPKMixin, TimestampMixin):
    __tablename__ = "knowledge_sources"

    project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id"), nullable=False)
    uploaded_by: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False)

    filename: Mapped[str] = mapped_column(String(500), nullable=False)
    original_filename: Mapped[str] = mapped_column(String(500), nullable=False)
    file_type: Mapped[FileType] = mapped_column(Enum(FileType, name="file_type"), nullable=False)
    storage_path: Mapped[str] = mapped_column(String(1000), nullable=False)
    file_size_bytes: Mapped[int] = mapped_column(BigInteger, default=0)

    tags: Mapped[list] = mapped_column(JSONB, default=list)
    category: Mapped[str | None] = mapped_column(String(100), nullable=True)
    source: Mapped[str | None] = mapped_column(String(500), nullable=True)

    status: Mapped[SourceStatus] = mapped_column(
        Enum(SourceStatus, name="source_status"), default=SourceStatus.uploaded
    )
    error_message: Mapped[str | None] = mapped_column(Text, nullable=True)

    extracted_text: Mapped["ExtractedText | None"] = relationship(
        back_populates="knowledge_source", uselist=False, cascade="all, delete-orphan"
    )


class ExtractedText(Base, UUIDPKMixin, TimestampMixin):
    __tablename__ = "extracted_texts"

    knowledge_source_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), ForeignKey("knowledge_sources.id"), nullable=False, unique=True
    )
    raw_text: Mapped[str | None] = mapped_column(Text, nullable=True)
    cleaned_text: Mapped[str | None] = mapped_column(Text, nullable=True)
    cleaning_report: Mapped[dict] = mapped_column(JSONB, default=dict)
    is_confirmed: Mapped[bool] = mapped_column(Boolean, default=False)

    knowledge_source: Mapped["KnowledgeSource"] = relationship(back_populates="extracted_text")
