"""Storage ingest job — server-side staging then R2 upload."""
from __future__ import annotations

import enum
from datetime import datetime
from typing import Optional
from uuid import UUID, uuid4

import sqlalchemy as sa
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, func
from sqlalchemy.dialects.postgresql import JSONB, UUID as PGUUID
from sqlalchemy.orm import Mapped, mapped_column

from src.core.database import Base


class StorageIngestJobStatus(str, enum.Enum):
    UPLOADING = "uploading"
    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"


class ArchivePathMode(str, enum.Enum):
    PRESERVE_FOLDERS = "preserve_folders"
    FLATTEN = "flatten"


class StorageIngestJob(Base):
    """Tracks a file uploaded to Core disk and processed into connector R2."""

    __tablename__ = "storage_ingest_jobs"

    id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
    project_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("projects.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    connection_id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("connector_connections.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    bucket_name: Mapped[str] = mapped_column(String(63), nullable=False)
    status: Mapped[str] = mapped_column(
        String(20),
        nullable=False,
        server_default=StorageIngestJobStatus.PENDING.value,
        index=True,
    )
    original_filename: Mapped[str] = mapped_column(String(512), nullable=False)
    staging_relative_path: Mapped[str] = mapped_column(String(1024), nullable=False)
    object_key: Mapped[Optional[str]] = mapped_column(String(1024), nullable=True)
    extract_archive: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, server_default=sa.false())
    is_archive: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, server_default=sa.false())
    archive_path_mode: Mapped[str] = mapped_column(
        String(32),
        nullable=False,
        server_default=ArchivePathMode.PRESERVE_FOLDERS.value,
    )
    uploaded_count: Mapped[int] = mapped_column(Integer, nullable=False, server_default="0")
    total_entries: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
    skipped_count: Mapped[int] = mapped_column(Integer, nullable=False, server_default="0")
    expected_size_bytes: Mapped[Optional[int]] = mapped_column(sa.BigInteger, nullable=True)
    received_bytes: Mapped[int] = mapped_column(sa.BigInteger, nullable=False, server_default="0")
    next_part_number: Mapped[int] = mapped_column(Integer, nullable=False, server_default="1")
    error: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    result_json: Mapped[Optional[dict]] = mapped_column(JSONB, 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(),
    )
