"""G21 — Polymorphic EvidenceAttachment.

Verifies SHA-256 dedupe, round-trip via attach_evidence, and pre_delete
cascade clean-up when a host is removed.
"""
from __future__ import annotations

import io
from decimal import Decimal

import pytest
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile

from apps.core.permissions.roles import UserRole
from apps.mel.indicators.models import (
    DataPoint,
    EvidenceAttachment,
    Indicator,
    IndicatorFrequency,
    LogFrame,
    LogFrameLevel,
    LogFrameRow,
)
from apps.mel.indicators.services import attach_evidence

User = get_user_model()

pytestmark = pytest.mark.django_db


@pytest.fixture
def officer():
    return User.objects.create_user(
        email="g21@example.com", password="x", role=UserRole.MEL_OFFICER,
    )


@pytest.fixture
def data_point():
    lf = LogFrame.objects.create(name="LF g21", slug="lf-g21")
    impact = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.IMPACT, title="I")
    outcome = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTCOME, title="O", parent=impact)
    output = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTPUT, title="P", parent=outcome)
    ind = Indicator.objects.create(
        logframe_row=output, code="g21-ind", name="G21", unit="count",
        calculation_method="count", data_source="manual",
        frequency=IndicatorFrequency.QUARTERLY,
    )
    return DataPoint.objects.create(indicator=ind, value=Decimal("1"), period_label="2026Q1")


def test_attach_evidence_to_datapoint(officer, data_point):
    f = SimpleUploadedFile("photo.png", b"png-bytes", content_type="image/png")
    att, created = attach_evidence(
        host=data_point, file=f, caption="field photo", uploaded_by=officer,
    )
    assert created is True
    assert att.content_hash
    ct = ContentType.objects.get_for_model(DataPoint)
    assert att.content_type == ct
    assert att.object_id == data_point.pk
    assert att.mime_type.startswith("image/")
    assert "field photo" in att.caption


def test_attach_evidence_idempotent_on_same_file_hash(officer, data_point):
    f1 = SimpleUploadedFile("photo.png", b"png-bytes", content_type="image/png")
    f2 = SimpleUploadedFile("photo2.png", b"png-bytes", content_type="image/png")
    a1, c1 = attach_evidence(host=data_point, file=f1, uploaded_by=officer)
    a2, c2 = attach_evidence(host=data_point, file=f2, uploaded_by=officer)
    assert c1 is True
    assert c2 is False
    assert a1.pk == a2.pk
    assert EvidenceAttachment.objects.filter(object_id=data_point.pk).count() == 1


def test_attach_evidence_distinct_hosts_are_independent(officer, data_point):
    lf = LogFrame.objects.create(name="LF g21-b", slug="lf-g21-b")
    impact = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.IMPACT, title="I2")
    outcome = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTCOME, title="O2", parent=impact)
    output = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTPUT, title="P2", parent=outcome)
    ind2 = Indicator.objects.create(
        logframe_row=output, code="g21-ind-b", name="G21-B", unit="count",
        calculation_method="count", data_source="manual",
        frequency=IndicatorFrequency.QUARTERLY,
    )
    dp2 = DataPoint.objects.create(indicator=ind2, value=Decimal("2"), period_label="2026Q1")

    f1 = SimpleUploadedFile("a.png", b"same-bytes", content_type="image/png")
    f2 = SimpleUploadedFile("a.png", b"same-bytes", content_type="image/png")
    a1, c1 = attach_evidence(host=data_point, file=f1, uploaded_by=officer)
    a2, c2 = attach_evidence(host=dp2, file=f2, uploaded_by=officer)
    assert c1 is True and c2 is True
    assert a1.pk != a2.pk


def test_pre_delete_purges_attachments_when_host_deleted(officer, data_point):
    f = SimpleUploadedFile("photo.png", b"unique-bytes", content_type="image/png")
    attach_evidence(host=data_point, file=f, uploaded_by=officer)
    assert EvidenceAttachment.objects.filter(object_id=data_point.pk).count() == 1
    data_point.delete()
    assert EvidenceAttachment.objects.filter(object_id=data_point.pk).count() == 0
