"""Tenant-scoped upload path tests.

These lock down the properties that make a storage key safe to hand to a
presigned URL: the organization is the leftmost segment, the filename carries no
content hint, and the stem is unpredictable.
"""

import pytest
from django.core.files.uploadedfile import SimpleUploadedFile

from apps.companies.models import Organization
from apps.core.uploads import UNASSIGNED_PREFIX, _safe_suffix, tenant_upload_path
from apps.disseminations.models import MediaAsset


@pytest.mark.django_db
class TestTenantUploadPath:
    def test_org_id_is_leftmost_segment(self):
        """Tenant boundary must lead the key so prefix-scoped credentials work."""
        org = Organization.objects.create(name="Path Org")
        asset = MediaAsset(organization=org)
        path = tenant_upload_path("media")(asset, "report.pdf")
        assert path.startswith(f"org/{org.id}/media/")

    def test_original_filename_is_not_in_key(self):
        """A name like 'Salary_Review_Budi.pdf' must not leak into the key."""
        org = Organization.objects.create(name="Path Org 2")
        path = tenant_upload_path("docs")(MediaAsset(organization=org), "Salary_Review_Budi.pdf")
        assert "Salary" not in path
        assert "Budi" not in path

    def test_extension_preserved(self):
        org = Organization.objects.create(name="Path Org 3")
        path = tenant_upload_path("docs")(MediaAsset(organization=org), "x.PDF")
        assert path.endswith(".pdf")

    def test_stem_is_unpredictable(self):
        """Two uploads in the same instant must not share a prefix.

        uuid7 (this project's PK default) would fail this: it encodes a
        millisecond timestamp in its leading bits, so near-simultaneous keys
        differ only in trailing randomness.
        """
        org = Organization.objects.create(name="Path Org 4")
        gen = tenant_upload_path("docs")
        keys = {gen(MediaAsset(organization=org), "a.pdf").split("/")[-1] for _ in range(50)}
        assert len(keys) == 50
        stems = [k.rsplit(".", 1)[0] for k in keys]
        # No two stems share even a 6-character prefix.
        assert len({s[:6] for s in stems}) == 50

    def test_unresolvable_org_falls_back_outside_every_tenant_prefix(self):
        """Must never land under some other tenant's org/<id>/ prefix."""
        path = tenant_upload_path("docs")(MediaAsset(), "x.pdf")
        assert path.startswith(f"org/{UNASSIGNED_PREFIX}/")

    @pytest.mark.parametrize("evil,expected", [
        ("../../etc/passwd", ""),
        ("x.tar.gz", ".gz"),
        ("no-extension", ""),
        ("weird.pdf%00.exe", ".exe"),  # %00 is in an earlier segment, correctly dropped
        ("a." + "x" * 40, ""),
    ])
    def test_suffix_cannot_inject_paths_or_junk(self, evil, expected):
        assert _safe_suffix(evil) == expected

    def test_path_has_no_traversal(self):
        org = Organization.objects.create(name="Path Org 5")
        path = tenant_upload_path("docs")(MediaAsset(organization=org), "../../../escape.pdf")
        assert ".." not in path

    def test_real_save_uses_tenant_path(self):
        """EmployeeDocument is one of the converted (high-sensitivity) models.

        MediaAsset deliberately still uses its original flat path — only hr,
        finance and administrations were converted in this change.
        """
        from apps.core.models import User
        from apps.hr.models import Employee, EmployeeDocument

        org = Organization.objects.create(name="Path Org 6")
        user = User.objects.create_user(email="pathtest@x.test", password="pw12345!")
        employee = Employee.objects.filter(user=user).first()
        if employee is None:
            employee = Employee.objects.create(
                user=user, organization=org, hire_date="2026-01-01"
            )
        else:
            employee.organization = org
            employee.save(update_fields=["organization"])

        doc = EmployeeDocument.objects.create(
            employee=employee,
            file=SimpleUploadedFile("Confidential Report.pdf", b"data"),
        )
        assert doc.file.name.startswith(f"org/{org.id}/employee-documents/")
        assert "Confidential" not in doc.file.name


class TestDeconstructible:
    def test_equal_instances_do_not_churn_migrations(self):
        """Unequal instances would emit a no-op AlterField on every run."""
        assert tenant_upload_path("docs") == tenant_upload_path("docs")
        assert tenant_upload_path("docs") != tenant_upload_path("other")

    def test_serializes_for_migrations(self):
        path, args, kwargs = tenant_upload_path("docs").deconstruct()
        assert path == "apps.core.uploads.tenant_upload_path"
        assert args == ("docs",)
