"""FSM and model invariants for repository documents."""

from __future__ import annotations

import pytest
from django.core.exceptions import ValidationError

from apps.repository.documents import services
from apps.repository.documents.models import Document


def _reload(doc: Document) -> Document:
    """Re-fetch from DB — FSMField(protected=True) forbids ``refresh_from_db``."""
    return Document.objects.get(pk=doc.pk)


@pytest.mark.django_db
def test_ingest_assigns_public_id_and_hash(submitter_user, public_collection, sample_pdf):
    doc = services.ingest_document(
        file=sample_pdf,
        title="Sample document",
        collection=public_collection,
        document_type=Document.DocumentType.THESIS,
        abstract="Test abstract.",
        submitted_by=submitter_user,
        actor=submitter_user,
    )
    assert doc.public_document_id.startswith("REPO-")
    assert doc.file_hash and len(doc.file_hash) == 64
    assert doc.status == Document.Status.DRAFT


@pytest.mark.django_db
def test_full_qa_happy_path(submitter_user, manager_user, public_collection, sample_pdf):
    doc = services.ingest_document(
        file=sample_pdf,
        title="QA happy path",
        collection=public_collection,
        document_type=Document.DocumentType.THESIS,
        abstract="Sufficient abstract for QA.",
        submitted_by=submitter_user,
        actor=submitter_user,
    )
    services.submit_for_qa(doc, actor=submitter_user)
    assert _reload(doc).status == Document.Status.UNDER_QA

    qa = doc.qa_records.first()
    services.decide_qa(qa, decision="approve", actor=manager_user, checklist=services.full_qa_checklist())
    fresh = _reload(doc)
    assert fresh.status == Document.Status.PUBLISHED
    assert fresh.published_at is not None


@pytest.mark.django_db
def test_revision_flow_returns_to_revision_required(submitter_user, manager_user, public_collection, sample_pdf):
    doc = services.ingest_document(
        file=sample_pdf,
        title="Needs revision",
        collection=public_collection,
        document_type=Document.DocumentType.THESIS,
        abstract="Abstract present.",
        submitted_by=submitter_user,
        actor=submitter_user,
    )
    services.submit_for_qa(doc, actor=submitter_user)
    qa = doc.qa_records.first()
    services.decide_qa(qa, decision="revision", comments="Add methods.", actor=manager_user)
    assert _reload(doc).status == Document.Status.REVISION_REQUIRED

    services.submit_for_qa(_reload(doc), actor=submitter_user)
    assert _reload(doc).status == Document.Status.UNDER_QA


@pytest.mark.django_db
def test_withdraw_requires_reason(submitter_user, manager_user, public_collection, sample_pdf):
    doc = services.ingest_document(
        file=sample_pdf,
        title="Withdraw test",
        collection=public_collection,
        document_type=Document.DocumentType.THESIS,
        abstract="Abstract.",
        submitted_by=submitter_user,
        actor=submitter_user,
    )
    services.submit_for_qa(doc, actor=submitter_user)
    services.decide_qa(
        doc.qa_records.first(),
        decision="approve",
        actor=manager_user,
        checklist=services.full_qa_checklist(),
    )

    with pytest.raises(ValidationError):
        services.withdraw_document(_reload(doc), reason="   ", actor=manager_user)

    services.withdraw_document(_reload(doc), reason="Outdated source data.", actor=manager_user)
    fresh = _reload(doc)
    assert fresh.status == Document.Status.WITHDRAWN
    assert fresh.withdrawn_reason == "Outdated source data."


@pytest.mark.django_db
def test_duplicate_detection(submitter_user, public_collection, sample_pdf):
    services.ingest_document(
        file=sample_pdf,
        title="First copy",
        collection=public_collection,
        document_type=Document.DocumentType.THESIS,
        abstract="A",
        submitted_by=submitter_user,
        actor=submitter_user,
    )
    sample_pdf.seek(0)
    with pytest.raises(services.DuplicateDocumentError):
        services.ingest_document(
            file=sample_pdf,
            title="Second copy (same bytes)",
            collection=public_collection,
            document_type=Document.DocumentType.THESIS,
            abstract="B",
            submitted_by=submitter_user,
            actor=submitter_user,
        )
