"""ExternalLink — an off-platform file-host link found in a post body.

Creators host the actual files (films, packs) on mega.nz / Google Drive /
MediaFire / Dropbox / Pixeldrain and drop the link in the post text. This row
is the record that the link existed (so nothing is silently dropped), the
dedup + dead-letter ledger for fetching it, and the driver the download worker
walks. `url` keeps the FULL link including the `#fragment` (mega's decryption
key) — truncating it makes the file undownloadable.

status lifecycle: pending → downloading → downloaded | failed | dead
(too many attempts) | skipped (host disabled). `attachment_id` links the
captured file once a download lands (SET NULL so deleting the attachment
doesn't delete the link record).
"""

from datetime import datetime

from sqlalchemy import (
    DateTime,
    Float,
    ForeignKey,
    Index,
    Integer,
    String,
    Text,
    func,
    text,
)
from sqlalchemy.orm import Mapped, mapped_column

from .base import Base

# Kept in sync with link_extract.SUPPORTED_HOSTS and the CHECK in migration 0049.
HOSTS = ("mega", "gdrive", "mediafire", "dropbox", "pixeldrain")
STATUSES = ("pending", "downloading", "downloaded", "failed", "skipped", "dead")


class ExternalLink(Base):
    __tablename__ = "external_link"
    __table_args__ = (
        # One row per (post, url). The full url (incl. #fragment) is the identity
        # — the same file linked twice in a post collapses to one row.
        Index("uq_external_link_post_url", "post_id", "url", unique=True),
        Index("ix_external_link_status", "status"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    post_id: Mapped[int] = mapped_column(
        ForeignKey("post.id", ondelete="CASCADE"), nullable=False, index=True
    )
    artist_id: Mapped[int | None] = mapped_column(
        ForeignKey("artist.id", ondelete="SET NULL"), nullable=True, index=True
    )
    host: Mapped[str] = mapped_column(String(16), nullable=False)
    url: Mapped[str] = mapped_column(Text, nullable=False)
    label: Mapped[str | None] = mapped_column(Text, nullable=True)
    status: Mapped[str] = mapped_column(
        String(16), nullable=False, server_default="pending"
    )
    attempts: Mapped[int] = mapped_column(
        Integer, nullable=False, server_default=text("0")
    )
    last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
    attachment_id: Mapped[int | None] = mapped_column(
        ForeignKey("post_attachment.id", ondelete="SET NULL"), nullable=True
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )
    completed_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    duration_seconds: Mapped[float | None] = mapped_column(Float, nullable=True)
