"""Tests for DMS Phase 10 — RECORDS MANAGEMENT.

Covers:
  * create_retention_policy — valid, empty name, negative days, invalid action
  * update_retention_policy — partial update, empty name guard
  * delete_retention_policy — soft-delete, excluded from list
  * place_legal_hold — valid, empty name guard
  * release_legal_hold — valid, double-release guard
  * has_active_legal_hold — True with active, False after release
  * create_archive_record — no policy, with policy (with/without period), permanent
  * create_archive_record — duplicate permanent guard
  * create_archive_record — with version FK
  * Queries: list_retention_policies (active_only), list_legal_holds (active_only),
    list_archive_records, list_expiring_archive_records, not-found raises
  * HTTP API: all endpoints for retention policies, legal holds, archive records
  * Permission gates: 401 unauthenticated, 403 no permission
"""

from __future__ import annotations

import uuid
from datetime import timedelta

import pytest

from simorgh.apps.dms.records import queries, services
from simorgh.apps.dms.records.iam_permissions import (
    PERM_ARCHIVE_CREATE,
    PERM_ARCHIVE_VIEW,
    PERM_LEGALHOLD_PLACE,
    PERM_LEGALHOLD_RELEASE,
    PERM_LEGALHOLD_VIEW,
    PERM_RETENTION_MANAGE,
    PERM_RETENTION_VIEW,
)
from simorgh.apps.dms.records.models import ActionOnExpiry, ArchiveRecord, LegalHold, RetentionPolicy
from simorgh.apps.dms.records.services import ArchiveError, LegalHoldError, RetentionPolicyError
from simorgh.apps.dms.common.exceptions import AssetNotFound
from simorgh.apps.dms.repositories.models import Repository
ALL_RECORDS_PERMS = frozenset({
    PERM_RETENTION_VIEW,
    PERM_RETENTION_MANAGE,
    PERM_LEGALHOLD_VIEW,
    PERM_LEGALHOLD_PLACE,
    PERM_LEGALHOLD_RELEASE,
    PERM_ARCHIVE_VIEW,
    PERM_ARCHIVE_CREATE,
})

READ_ONLY_PERMS = frozenset({
    PERM_RETENTION_VIEW,
    PERM_LEGALHOLD_VIEW,
    PERM_ARCHIVE_VIEW,
})


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------

@pytest.fixture
def alice_membership(alice, tenant_acme, acme_tree, role_admin):
    """Override conftest alice_membership — alice gets role_admin (all perms)."""
    from simorgh.apps.memberships.models import Membership

    m = Membership.objects.create(
        tenant=tenant_acme,
        organization_node=acme_tree["root"],
        role=role_admin,
    )
    m.users.add(alice)
    return m


@pytest.fixture
def charlie(db):
    """A plain non-superuser without membership — for 403 tests."""
    from django.contrib.auth import get_user_model

    User = get_user_model()
    return User.objects.create_user(
        "+989000000077", password="x", email="charlie_records@example.com",
    )


@pytest.fixture
def repo(tenant_acme, acme_tree):
    return Repository.objects.create(
        name="Records Repo",
        slug="records-repo",
        tenant=tenant_acme,
        organization_node=acme_tree["root"],
    )


@pytest.fixture
def doc(repo, tenant_acme, acme_tree):
    from simorgh.apps.dms.documents.models import Document

    return Document.objects.create(
        title="Records Document",
        repository=repo,
        tenant=tenant_acme,
        organization_node=acme_tree["root"],
    )


@pytest.fixture
def file_asset(tenant_acme, acme_tree):
    from simorgh.apps.dms.assets.constants import APP_CONTEXT
    from simorgh.apps.storage.models import FileMetadata, FileUploadStatus

    return FileMetadata.objects.create(
        filename="doc.pdf",
        content_type="application/pdf",
        size_bytes=1024,
        path=f"test/records/{uuid.uuid4()}.pdf",
        tenant=tenant_acme,
        organization_node=acme_tree["root"],
        upload_status=FileUploadStatus.READY,
        app_context=APP_CONTEXT,
    )


@pytest.fixture
def version(doc, file_asset, tenant_acme, acme_tree):
    from simorgh.apps.dms.documents.services import add_version

    return add_version(
        document=doc,
        file_asset=file_asset,
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
    )


@pytest.fixture
def retention_policy(tenant_acme, acme_tree):
    return services.create_retention_policy(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        name="Standard 7-Year",
        retention_period_days=2555,
        action_on_expiry=ActionOnExpiry.REVIEW,
    )


# ---------------------------------------------------------------------------
# Service — create_retention_policy
# ---------------------------------------------------------------------------

@pytest.mark.django_db
def test_create_retention_policy_valid(tenant_acme, acme_tree):
    policy = services.create_retention_policy(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        name="7-Year Retention",
        description="Keep for 7 years",
        retention_period_days=2555,
        action_on_expiry=ActionOnExpiry.REVIEW,
    )
    assert policy.pk is not None
    assert policy.name == "7-Year Retention"
    assert policy.retention_period_days == 2555
    assert policy.action_on_expiry == ActionOnExpiry.REVIEW
    assert policy.is_active is True
    assert policy.is_deleted is False


@pytest.mark.django_db
def test_create_retention_policy_empty_name_raises(tenant_acme, acme_tree):
    with pytest.raises(RetentionPolicyError, match="empty"):
        services.create_retention_policy(
            tenant_id=tenant_acme.pk,
            organization_node_id=acme_tree["root"].pk,
            name="   ",
            retention_period_days=365,
        )


@pytest.mark.django_db
def test_create_retention_policy_negative_days_raises(tenant_acme, acme_tree):
    with pytest.raises(RetentionPolicyError, match="non-negative"):
        services.create_retention_policy(
            tenant_id=tenant_acme.pk,
            organization_node_id=acme_tree["root"].pk,
            name="Bad Policy",
            retention_period_days=-1,
        )


@pytest.mark.django_db
def test_create_retention_policy_invalid_action_raises(tenant_acme, acme_tree):
    with pytest.raises(RetentionPolicyError, match="Invalid action_on_expiry"):
        services.create_retention_policy(
            tenant_id=tenant_acme.pk,
            organization_node_id=acme_tree["root"].pk,
            name="Bad Action Policy",
            action_on_expiry="explode",
        )


@pytest.mark.django_db
def test_create_retention_policy_zero_period(tenant_acme, acme_tree):
    """Zero period means keep indefinitely."""
    policy = services.create_retention_policy(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        name="Indefinite Hold",
        retention_period_days=0,
        action_on_expiry=ActionOnExpiry.ARCHIVE,
    )
    assert policy.retention_period_days == 0


# ---------------------------------------------------------------------------
# Service — update_retention_policy
# ---------------------------------------------------------------------------

@pytest.mark.django_db
def test_update_retention_policy(retention_policy):
    updated = services.update_retention_policy(
        retention_policy,
        name="Updated Name",
        retention_period_days=1825,
        action_on_expiry=ActionOnExpiry.DESTROY,
        is_active=False,
    )
    assert updated.name == "Updated Name"
    assert updated.retention_period_days == 1825
    assert updated.action_on_expiry == ActionOnExpiry.DESTROY
    assert updated.is_active is False


@pytest.mark.django_db
def test_update_retention_policy_empty_name_raises(retention_policy):
    with pytest.raises(RetentionPolicyError, match="empty"):
        services.update_retention_policy(retention_policy, name="")


@pytest.mark.django_db
def test_update_retention_policy_partial(retention_policy):
    """Updating only one field leaves others unchanged."""
    original_days = retention_policy.retention_period_days
    services.update_retention_policy(retention_policy, name="New Name")
    retention_policy.refresh_from_db()
    assert retention_policy.name == "New Name"
    assert retention_policy.retention_period_days == original_days


# ---------------------------------------------------------------------------
# Service — delete_retention_policy
# ---------------------------------------------------------------------------

@pytest.mark.django_db
def test_delete_retention_policy_soft_deletes(retention_policy):
    services.delete_retention_policy(retention_policy)
    retention_policy.refresh_from_db()
    assert retention_policy.is_deleted is True


@pytest.mark.django_db
def test_deleted_policy_excluded_from_list(tenant_acme, acme_tree, retention_policy):
    services.delete_retention_policy(retention_policy)
    qs = queries.list_retention_policies(tenant_acme.pk)
    assert not qs.filter(pk=retention_policy.pk).exists()


# ---------------------------------------------------------------------------
# Service — place_legal_hold
# ---------------------------------------------------------------------------

@pytest.mark.django_db
def test_place_legal_hold_valid(alice, doc, tenant_acme, acme_tree):
    hold = services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="Litigation Hold #001",
        placed_by=alice,
        notes="Pending discovery review",
    )
    assert hold.pk is not None
    assert hold.name == "Litigation Hold #001"
    assert hold.placed_by_id == alice.pk
    assert hold.ended_at is None
    assert hold.is_active is True


@pytest.mark.django_db
def test_place_legal_hold_empty_name_raises(alice, doc, tenant_acme, acme_tree):
    with pytest.raises(LegalHoldError, match="empty"):
        services.place_legal_hold(
            tenant_id=tenant_acme.pk,
            organization_node_id=acme_tree["root"].pk,
            document=doc,
            name="",
            placed_by=alice,
        )


@pytest.mark.django_db
def test_multiple_concurrent_holds_allowed(alice, doc, tenant_acme, acme_tree):
    """A document can have more than one active hold simultaneously."""
    hold1 = services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="Hold 1",
        placed_by=alice,
    )
    hold2 = services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="Hold 2",
        placed_by=alice,
    )
    assert hold1.pk != hold2.pk
    assert queries.has_active_legal_hold(doc) is True


# ---------------------------------------------------------------------------
# Service — release_legal_hold
# ---------------------------------------------------------------------------

@pytest.mark.django_db
def test_release_legal_hold_valid(alice, doc, tenant_acme, acme_tree):
    hold = services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="Litigation Hold",
        placed_by=alice,
    )
    released = services.release_legal_hold(hold, released_by=alice)
    assert released.ended_at is not None
    assert released.released_by_id == alice.pk
    assert released.is_active is False


@pytest.mark.django_db
def test_release_already_released_raises(alice, doc, tenant_acme, acme_tree):
    hold = services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="Hold",
        placed_by=alice,
    )
    services.release_legal_hold(hold, released_by=alice)
    with pytest.raises(LegalHoldError, match="already released"):
        services.release_legal_hold(hold, released_by=alice)


@pytest.mark.django_db
def test_has_active_legal_hold_false_after_all_released(alice, doc, tenant_acme, acme_tree):
    hold = services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="Solo Hold",
        placed_by=alice,
    )
    assert queries.has_active_legal_hold(doc) is True
    services.release_legal_hold(hold, released_by=alice)
    assert queries.has_active_legal_hold(doc) is False


# ---------------------------------------------------------------------------
# Service — create_archive_record
# ---------------------------------------------------------------------------

@pytest.mark.django_db
def test_create_archive_record_no_policy(alice, doc, tenant_acme, acme_tree):
    record = services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        reason="End of project",
    )
    assert record.pk is not None
    assert record.archived_at is not None
    assert record.retention_policy is None
    assert record.expires_at is None
    assert record.is_permanent is False


@pytest.mark.django_db
def test_create_archive_record_with_policy_computes_expiry(
    alice, doc, retention_policy, tenant_acme, acme_tree
):
    """expires_at = archived_at + retention_period_days."""
    record = services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        retention_policy=retention_policy,
    )
    assert record.expires_at is not None
    expected_delta = timedelta(days=retention_policy.retention_period_days)
    delta = record.expires_at - record.archived_at
    # Allow 1-second tolerance
    assert abs(delta.total_seconds() - expected_delta.total_seconds()) < 1


@pytest.mark.django_db
def test_create_archive_record_zero_period_no_expiry(alice, doc, tenant_acme, acme_tree):
    """Policy with period=0 → expires_at=None."""
    zero_policy = services.create_retention_policy(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        name="No Expiry Policy",
        retention_period_days=0,
    )
    record = services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        retention_policy=zero_policy,
    )
    assert record.expires_at is None


@pytest.mark.django_db
def test_create_archive_record_permanent(alice, doc, tenant_acme, acme_tree, retention_policy):
    """is_permanent=True → expires_at always None even with a policy."""
    record = services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        retention_policy=retention_policy,
        is_permanent=True,
    )
    assert record.is_permanent is True
    assert record.expires_at is None


@pytest.mark.django_db
def test_create_archive_record_duplicate_permanent_raises(
    alice, doc, tenant_acme, acme_tree
):
    services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        is_permanent=True,
    )
    with pytest.raises(ArchiveError, match="already has a permanent archive record"):
        services.create_archive_record(
            tenant_id=tenant_acme.pk,
            organization_node_id=acme_tree["root"].pk,
            document=doc,
            archived_by=alice,
            is_permanent=True,
        )


@pytest.mark.django_db
def test_create_archive_record_with_version(alice, doc, version, tenant_acme, acme_tree):
    record = services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        version=version,
    )
    assert record.version_id == version.pk


# ---------------------------------------------------------------------------
# Queries
# ---------------------------------------------------------------------------

@pytest.mark.django_db
def test_list_retention_policies_active_only(tenant_acme, acme_tree):
    p1 = services.create_retention_policy(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        name="Active Policy",
        is_active=True,
    )
    p2 = services.create_retention_policy(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        name="Inactive Policy",
        is_active=False,
    )
    active = list(queries.list_retention_policies(tenant_acme.pk, active_only=True))
    all_policies = list(queries.list_retention_policies(tenant_acme.pk, active_only=False))
    assert p1 in active
    assert p2 not in active
    assert p2 in all_policies


@pytest.mark.django_db
def test_list_legal_holds_active_only(alice, doc, tenant_acme, acme_tree):
    hold1 = services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="Active Hold",
        placed_by=alice,
    )
    hold2 = services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="Released Hold",
        placed_by=alice,
    )
    services.release_legal_hold(hold2, released_by=alice)

    active = list(queries.list_legal_holds_for_document(doc, active_only=True))
    all_holds = list(queries.list_legal_holds_for_document(doc, active_only=False))

    assert hold1 in active
    assert hold2 not in active
    assert hold2 in all_holds


@pytest.mark.django_db
def test_list_archive_records_for_document(alice, doc, tenant_acme, acme_tree):
    r1 = services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        reason="First archive",
    )
    r2 = services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        reason="Second archive",
    )
    records = list(queries.list_archive_records_for_document(doc))
    pks = [r.pk for r in records]
    assert r1.pk in pks
    assert r2.pk in pks


@pytest.mark.django_db
def test_list_expiring_archive_records(alice, doc, retention_policy, tenant_acme, acme_tree):
    from django.utils import timezone

    record = services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        retention_policy=retention_policy,
    )
    # Record with far future expiry — should NOT appear
    far_policy = services.create_retention_policy(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        name="1000-Year Policy",
        retention_period_days=365000,
    )
    services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        retention_policy=far_policy,
    )

    # Query records expiring within the next 10 years
    before = timezone.now() + timedelta(days=3650)
    expiring = list(
        queries.list_expiring_archive_records(tenant_acme.pk, before=before)
    )
    assert record in expiring


@pytest.mark.django_db
def test_get_retention_policy_not_found_raises(tenant_acme):
    with pytest.raises(AssetNotFound):
        queries.get_retention_policy(tenant_acme.pk, str(uuid.uuid4()))


@pytest.mark.django_db
def test_get_legal_hold_not_found_raises(tenant_acme):
    with pytest.raises(AssetNotFound):
        queries.get_legal_hold(tenant_acme.pk, str(uuid.uuid4()))


@pytest.mark.django_db
def test_get_archive_record_not_found_raises(tenant_acme):
    with pytest.raises(AssetNotFound):
        queries.get_archive_record(tenant_acme.pk, str(uuid.uuid4()))


# ---------------------------------------------------------------------------
# HTTP API — Retention Policies
# ---------------------------------------------------------------------------

def _url(path):
    return f"/api/v1/dms/{path}"


@pytest.mark.django_db
def test_api_list_retention_policies(
    client, alice, alice_membership, tenant_acme, acme_tree
):
    services.create_retention_policy(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        name="Policy A",
    )
    client.force_login(alice)
    resp = client.get(_url("retention-policies/"), HTTP_X_TENANT=tenant_acme.slug)
    assert resp.status_code == 200
    names = [p["name"] for p in resp.json()]
    assert "Policy A" in names


@pytest.mark.django_db
def test_api_create_retention_policy(client, alice, alice_membership, tenant_acme, acme_tree):
    client.force_login(alice)
    resp = client.post(
        _url("retention-policies/"),
        data={
            "name": "New Policy",
            "retention_period_days": 365,
            "action_on_expiry": "archive",
        },
        content_type="application/json",
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 201
    data = resp.json()
    assert data["name"] == "New Policy"
    assert data["retention_period_days"] == 365


@pytest.mark.django_db
def test_api_get_retention_policy(client, alice, alice_membership, tenant_acme, acme_tree, retention_policy):
    client.force_login(alice)
    resp = client.get(
        _url(f"retention-policies/{retention_policy.public_id}/"),
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 200
    assert resp.json()["name"] == retention_policy.name


@pytest.mark.django_db
def test_api_update_retention_policy(client, alice, alice_membership, tenant_acme, acme_tree, retention_policy):
    client.force_login(alice)
    resp = client.put(
        _url(f"retention-policies/{retention_policy.public_id}/"),
        data={"name": "Renamed Policy"},
        content_type="application/json",
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 200
    assert resp.json()["name"] == "Renamed Policy"


@pytest.mark.django_db
def test_api_delete_retention_policy(client, alice, alice_membership, tenant_acme, acme_tree, retention_policy):
    client.force_login(alice)
    resp = client.delete(
        _url(f"retention-policies/{retention_policy.public_id}/"),
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 204
    retention_policy.refresh_from_db()
    assert retention_policy.is_deleted is True


# ---------------------------------------------------------------------------
# HTTP API — Legal Holds
# ---------------------------------------------------------------------------

@pytest.mark.django_db
def test_api_list_legal_holds(client, alice, alice_membership, tenant_acme, acme_tree, doc):
    services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="API Hold",
        placed_by=alice,
    )
    client.force_login(alice)
    resp = client.get(
        _url(f"documents/{doc.public_id}/legal-holds/"),
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 200
    names = [h["name"] for h in resp.json()]
    assert "API Hold" in names


@pytest.mark.django_db
def test_api_place_legal_hold(client, alice, alice_membership, tenant_acme, acme_tree, doc):
    client.force_login(alice)
    resp = client.post(
        _url(f"documents/{doc.public_id}/legal-holds/"),
        data={"name": "New Hold", "notes": "Test hold"},
        content_type="application/json",
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 201
    data = resp.json()
    assert data["name"] == "New Hold"
    assert data["is_active"] is True


@pytest.mark.django_db
def test_api_get_legal_hold(client, alice, alice_membership, tenant_acme, acme_tree, doc):
    hold = services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="Fetch Hold",
        placed_by=alice,
    )
    client.force_login(alice)
    resp = client.get(
        _url(f"documents/{doc.public_id}/legal-holds/{hold.public_id}/"),
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 200
    assert resp.json()["name"] == "Fetch Hold"


@pytest.mark.django_db
def test_api_release_legal_hold(client, alice, alice_membership, tenant_acme, acme_tree, doc):
    hold = services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="Release Me",
        placed_by=alice,
    )
    client.force_login(alice)
    resp = client.post(
        _url(f"documents/{doc.public_id}/legal-holds/{hold.public_id}/release/"),
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 200
    assert resp.json()["is_active"] is False


@pytest.mark.django_db
def test_api_release_hold_already_released_returns_400(
    client, alice, alice_membership, tenant_acme, acme_tree, doc
):
    hold = services.place_legal_hold(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        name="Double Release",
        placed_by=alice,
    )
    services.release_legal_hold(hold, released_by=alice)

    client.force_login(alice)
    resp = client.post(
        _url(f"documents/{doc.public_id}/legal-holds/{hold.public_id}/release/"),
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 400


# ---------------------------------------------------------------------------
# HTTP API — Archive Records
# ---------------------------------------------------------------------------

@pytest.mark.django_db
def test_api_list_archive_records(client, alice, alice_membership, tenant_acme, acme_tree, doc):
    services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        reason="Initial archive",
    )
    client.force_login(alice)
    resp = client.get(
        _url(f"documents/{doc.public_id}/archive-records/"),
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 200
    assert len(resp.json()) >= 1


@pytest.mark.django_db
def test_api_create_archive_record(client, alice, alice_membership, tenant_acme, acme_tree, doc):
    client.force_login(alice)
    resp = client.post(
        _url(f"documents/{doc.public_id}/archive-records/"),
        data={"reason": "Compliance archive"},
        content_type="application/json",
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 201
    data = resp.json()
    assert data["reason"] == "Compliance archive"
    assert data["expires_at"] is None
    assert data["is_permanent"] is False


@pytest.mark.django_db
def test_api_create_archive_record_with_policy(
    client, alice, alice_membership, tenant_acme, acme_tree, doc, retention_policy
):
    client.force_login(alice)
    resp = client.post(
        _url(f"documents/{doc.public_id}/archive-records/"),
        data={
            "reason": "Policy archive",
            "retention_policy_id": str(retention_policy.public_id),
        },
        content_type="application/json",
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 201
    data = resp.json()
    assert data["retention_policy_id"] == str(retention_policy.public_id)
    assert data["expires_at"] is not None


@pytest.mark.django_db
def test_api_get_archive_record(client, alice, alice_membership, tenant_acme, acme_tree, doc):
    record = services.create_archive_record(
        tenant_id=tenant_acme.pk,
        organization_node_id=acme_tree["root"].pk,
        document=doc,
        archived_by=alice,
        reason="Get test",
    )
    client.force_login(alice)
    resp = client.get(
        _url(f"documents/{doc.public_id}/archive-records/{record.public_id}/"),
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 200
    assert resp.json()["reason"] == "Get test"


@pytest.mark.django_db
def test_api_archive_record_not_found(client, alice, alice_membership, tenant_acme, acme_tree, doc):
    client.force_login(alice)
    resp = client.get(
        _url(f"documents/{doc.public_id}/archive-records/{uuid.uuid4()}/"),
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 404


# ---------------------------------------------------------------------------
# Permission / Auth gates
# ---------------------------------------------------------------------------

@pytest.mark.django_db
def test_api_list_policies_unauthenticated(tenant_acme):
    from django.test import Client

    anon_client = Client()
    resp = anon_client.get(_url("retention-policies/"), HTTP_X_TENANT=tenant_acme.slug)
    assert resp.status_code == 401


@pytest.mark.django_db
def test_api_create_policy_no_perm_returns_403(client, charlie, tenant_acme, acme_tree):
    """User with no membership (empty permissions) cannot create a policy."""
    client.force_login(charlie)
    resp = client.post(
        _url("retention-policies/"),
        data={"name": "Blocked Policy"},
        content_type="application/json",
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 403


@pytest.mark.django_db
def test_api_place_hold_no_perm_returns_403(client, charlie, tenant_acme, acme_tree, doc):
    """User with no membership cannot place a legal hold."""
    client.force_login(charlie)
    resp = client.post(
        _url(f"documents/{doc.public_id}/legal-holds/"),
        data={"name": "Blocked Hold"},
        content_type="application/json",
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 403


@pytest.mark.django_db
def test_api_create_archive_no_perm_returns_403(client, charlie, tenant_acme, acme_tree, doc):
    """User with no membership cannot create an archive record."""
    client.force_login(charlie)
    resp = client.post(
        _url(f"documents/{doc.public_id}/archive-records/"),
        data={"reason": "blocked"},
        content_type="application/json",
        HTTP_X_TENANT=tenant_acme.slug,
    )
    assert resp.status_code == 403

