"""Phase 2.5 — auto-classification rule engine (FRREP-MCL006 / SRS A3).

``MetadataSchema.auto_classification_rules`` ({"if_keyword","then_collection_slug"})
are evaluated against a document's keywords / title / abstract to *suggest* a
target collection. The suggestion is advisory: the user accepts or overrides it,
and an override is written to the audit trail.
"""
from __future__ import annotations

import pytest
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import Client
from django.urls import reverse

from apps.core.audit import tasks as audit_tasks
from apps.core.audit.models import AuditLog
from apps.repository.documents import services
from apps.repository.documents.models import Collection, Document, MetadataSchema

PDF_BYTES = (
    b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n1 0 obj<</Type/Catalog>>endobj\ntrailer<<>>\n%%EOF\n"
)


@pytest.fixture(autouse=True)
def sync_audit(monkeypatch):
    def _eager_delay(**kwargs):
        return audit_tasks.persist_audit_log(**kwargs)

    monkeypatch.setattr(audit_tasks.write_audit_log, "delay", _eager_delay)


@pytest.fixture
def datasets_collection(db, manager_user):
    col = Collection.objects.create(
        name="Datasets", slug="datasets",
        visibility=Collection.Visibility.PUBLIC_OPEN,
    )
    col.managed_by.add(manager_user)
    return col


@pytest.fixture
def classification_schema(db, datasets_collection):
    return MetadataSchema.objects.create(
        name="Routing rules",
        auto_classification_rules=[
            {"if_keyword": "dataset", "then_collection_slug": "datasets"},
        ],
    )


# ── Rule evaluation ────────────────────────────────────────────────────────


@pytest.mark.django_db
def test_evaluate_matches_keyword(datasets_collection, classification_schema):
    result = services.evaluate_auto_classification(keywords=["dataset", "maize"])
    assert result == datasets_collection


@pytest.mark.django_db
def test_evaluate_matches_title_substring(datasets_collection, classification_schema):
    result = services.evaluate_auto_classification(
        keywords=[], title="A new dataset of soil samples"
    )
    assert result == datasets_collection


@pytest.mark.django_db
def test_evaluate_no_match_returns_none(datasets_collection, classification_schema):
    assert services.evaluate_auto_classification(keywords=["policy"], title="Brief") is None


@pytest.mark.django_db
def test_evaluate_ignores_rule_with_missing_collection(db):
    MetadataSchema.objects.create(
        name="Dangling",
        auto_classification_rules=[
            {"if_keyword": "ghost", "then_collection_slug": "does-not-exist"},
        ],
    )
    assert services.evaluate_auto_classification(keywords=["ghost"]) is None


# ── Suggestion endpoint ────────────────────────────────────────────────────


@pytest.mark.django_db
def test_classify_suggest_endpoint(datasets_collection, classification_schema, submitter_user):
    client = Client()
    client.force_login(submitter_user)
    resp = client.get(reverse("repo_documents:classify_suggest"), {"keywords": "dataset"})
    assert resp.status_code == 200
    suggested = resp.json()["suggested"]
    assert suggested["slug"] == "datasets"


@pytest.mark.django_db
def test_classify_suggest_silent_when_already_in_target(
    datasets_collection, classification_schema, submitter_user
):
    client = Client()
    client.force_login(submitter_user)
    resp = client.get(
        reverse("repo_documents:classify_suggest"),
        {"keywords": "dataset", "collection": str(datasets_collection.pk)},
    )
    assert resp.json()["suggested"] is None


# ── Override auditing ──────────────────────────────────────────────────────


@pytest.mark.django_db
def test_override_is_audited_on_submit(
    public_collection, datasets_collection, classification_schema, submitter_user,
    django_capture_on_commit_callbacks,
):
    """The wizard suggested 'datasets' but the user kept 'theses' — log the override.

    Audit writes route through ``transaction.on_commit``; execute the captured
    callbacks so the row lands in the test DB.
    """
    client = Client()
    client.force_login(submitter_user)
    data = {
        "title": "A dataset of soil samples",
        "abstract": "raw data",
        "document_type": Document.DocumentType.DATASET,
        "license_type": Document.License.CC_BY,
        "language": "en",
        "year": 2026,
        "collection": public_collection.pk,  # 'theses', not the suggested 'datasets'
        "authors_text": "Kato, Amina",
        "keywords_text": "dataset",
        "suggested_collection_slug": "datasets",
        "file": SimpleUploadedFile("t.pdf", PDF_BYTES, content_type="application/pdf"),
    }
    with django_capture_on_commit_callbacks(execute=True):
        resp = client.post(reverse("repo_documents:submit"), data=data)
    assert resp.status_code == 302
    overrides = AuditLog.objects.filter(
        target_app="repository_documents",
        changes__auto_classification_overridden=True,
    )
    assert overrides.exists()


@pytest.mark.django_db
def test_no_override_audit_when_choice_matches_suggestion(
    datasets_collection, classification_schema, submitter_user,
    django_capture_on_commit_callbacks,
):
    client = Client()
    client.force_login(submitter_user)
    data = {
        "title": "A dataset of soil samples",
        "abstract": "raw data",
        "document_type": Document.DocumentType.DATASET,
        "license_type": Document.License.CC_BY,
        "language": "en",
        "year": 2026,
        "collection": datasets_collection.pk,  # accepted the suggestion
        "authors_text": "Kato, Amina",
        "keywords_text": "dataset",
        "suggested_collection_slug": "datasets",
        "file": SimpleUploadedFile("t.pdf", PDF_BYTES, content_type="application/pdf"),
    }
    with django_capture_on_commit_callbacks(execute=True):
        resp = client.post(reverse("repo_documents:submit"), data=data)
    assert resp.status_code == 302
    assert not AuditLog.objects.filter(
        target_app="repository_documents",
        changes__auto_classification_overridden=True,
    ).exists()
