"""The research-package import contract: validation, idempotency by slug, and
the zip-slip guard."""

from tests.conftest import as_user, import_package, make_package


def test_import_creates_draft_options_with_screenshots(client, seed):
    as_user(client, "t_admin")
    resp = import_package(
        client,
        seed.project,
        [
            {"slug": "alpha", "display_label": "Option A"},
            {"slug": "beta", "display_label": "Option B"},
        ],
    )
    assert resp.status_code == 200, resp.text
    body = resp.json()
    assert set(body["created"]) == {"alpha", "beta"}
    assert body["updated"] == []

    listing = client.get(f"/api/projects/{seed.project.id}/options").json()
    by_slug = {row["slug"]: row for row in listing}
    for slug in ("alpha", "beta"):
        assert by_slug[slug]["status"] == "draft"
        assert by_slug[slug]["has_desktop"] is True
        assert by_slug[slug]["has_mobile"] is True


def test_reimport_updates_and_preserves_review(client, seed):
    as_user(client, "t_admin")
    first = import_package(client, seed.project, [{"slug": "alpha", "descriptor": "v1"}])
    assert first.status_code == 200
    listing = client.get(f"/api/projects/{seed.project.id}/options").json()
    option_id = next(o["id"] for o in listing if o["slug"] == "alpha")
    publish = client.patch(
        f"/api/projects/{seed.project.id}/options/{option_id}", json={"status": "published"}
    )
    assert publish.status_code == 200

    as_user(client, "t_member")
    rate = client.patch(
        f"/api/projects/{seed.project.id}/review/options/{option_id}", json={"rating": 3}
    )
    assert rate.status_code == 200

    as_user(client, "t_admin")
    second = import_package(client, seed.project, [{"slug": "alpha", "descriptor": "v2"}])
    assert second.status_code == 200, second.text
    body = second.json()
    assert body["created"] == []
    assert body["updated"] == ["alpha"]

    listing_after = client.get(f"/api/projects/{seed.project.id}/options").json()
    row = next(o for o in listing_after if o["slug"] == "alpha")
    assert row["status"] == "published"
    assert row["descriptor"] == "v2"

    as_user(client, "t_member")
    bundle = client.get(f"/api/projects/{seed.project.id}/review").json()
    my_review = next(r for r in bundle["my_reviews"] if r["option_id"] == option_id)
    assert my_review["rating"] == 3


def test_import_zip_slip_relative_traversal_rejected(client, seed):
    as_user(client, "t_admin")
    payload = make_package(
        seed.project.slug,
        [{"slug": "alpha"}],
        extra_entries={"../evil.png": b"payload"},
    )
    files = {"package": ("package.zip", payload, "application/zip")}
    resp = client.post(f"/api/projects/{seed.project.id}/import", files=files)
    assert resp.status_code == 422
    assert resp.json()["detail"]["error_code"] == "UNSAFE_ZIP_ENTRY"

    listing = client.get(f"/api/projects/{seed.project.id}/options").json()
    assert listing == []


def test_import_zip_slip_absolute_path_rejected(client, seed):
    as_user(client, "t_admin")
    payload = make_package(
        seed.project.slug,
        [{"slug": "alpha"}],
        extra_entries={"/abs.png": b"payload"},
    )
    files = {"package": ("package.zip", payload, "application/zip")}
    resp = client.post(f"/api/projects/{seed.project.id}/import", files=files)
    assert resp.status_code == 422
    assert resp.json()["detail"]["error_code"] == "UNSAFE_ZIP_ENTRY"

    listing = client.get(f"/api/projects/{seed.project.id}/options").json()
    assert listing == []


def test_import_unsupported_schema_version_rejected(client, seed):
    as_user(client, "t_admin")
    resp = import_package(client, seed.project, [{"slug": "alpha"}], schema_version=2)
    assert resp.status_code == 422
    assert resp.json()["detail"]["error_code"] == "UNSUPPORTED_SCHEMA_VERSION"


def test_import_project_slug_mismatch_rejected(client, seed):
    as_user(client, "t_admin")
    payload = make_package("some-other-project-slug", [{"slug": "alpha"}])
    files = {"package": ("package.zip", payload, "application/zip")}
    resp = client.post(f"/api/projects/{seed.project.id}/import", files=files)
    assert resp.status_code == 422
    assert resp.json()["detail"]["error_code"] == "PROJECT_SLUG_MISMATCH"


def test_import_missing_mobile_screenshot_rejected(client, seed):
    as_user(client, "t_admin")
    resp = import_package(client, seed.project, [{"slug": "alpha"}], omit_screenshot=True)
    assert resp.status_code == 422
    assert resp.json()["detail"]["error_code"] == "SCREENSHOT_MISSING"


def test_import_duplicate_slug_rejected(client, seed):
    as_user(client, "t_admin")
    resp = import_package(client, seed.project, [{"slug": "dup"}, {"slug": "dup"}])
    assert resp.status_code == 422
    assert resp.json()["detail"]["error_code"] == "OPTION_SLUG_DUPLICATE"


def test_import_not_a_zip_rejected(client, seed):
    as_user(client, "t_admin")
    files = {"package": ("package.zip", b"hello", "application/zip")}
    resp = client.post(f"/api/projects/{seed.project.id}/import", files=files)
    assert resp.status_code == 422
    assert resp.json()["detail"]["error_code"] == "NOT_A_ZIP"


def test_import_forbidden_for_member(client, seed):
    as_user(client, "t_member")
    resp = import_package(client, seed.project, [{"slug": "alpha"}])
    assert resp.status_code == 403
    assert resp.json()["detail"]["error_code"] == "PERMISSION_REQUIRED"
