"""Independent two-stage r71b package derivation tamper matrix."""

from __future__ import annotations

import json
from datetime import date, datetime

import pytest
from pydantic import JsonValue

from gateway.platforms.nutrition_weekly_maintenance_contract import (
    Topic59MaintenanceScopeV1,
    canonical_document,
    canonical_json,
)
from scripts.nutricoach_v150_r71b_package import (
    PackageDerivationError,
    derive_r71b_package,
    verify_r71b_package_derivation,
)


def _scope() -> Topic59MaintenanceScopeV1:
    return Topic59MaintenanceScopeV1(
        candidate_digest="1" * 64,
        config_digest="2" * 64,
        route_digest="3" * 64,
        customer_identity_digest="4" * 64,
        card_slot="5" * 64,
        kst_day=date(2026, 9, 1),
        not_before=datetime.fromisoformat("2026-09-01T16:00:00+09:00"),
        expires_at=datetime.fromisoformat("2026-09-01T16:10:00+09:00"),
    )


def _inputs(scope: Topic59MaintenanceScopeV1) -> dict[str, JsonValue]:
    return {
        "schema": "nutricoach-v150-r71b-package-binding-inputs-v1",
        "product_generation": "r71",
        "package_namespace": "r71b-maintenance",
        "authority_id": "nutricoach-v150-v15-runtime-authority-r71b-maintenance",
        "paths": {"observer_root": "/tmp/observer-r71"},
        "candidate": {"candidate_digest": "1" * 64},
        "wheels": [],
        "controller": {"files": {}},
        "protected_inventory": {"explicit_maintenance_volatile_paths": []},
        "dependency_snapshot": {"dependency_snapshot_sha256": "6" * 64},
        "authority_baseline": {"candidate_digest": "7" * 64},
        "target_binding_base": {"profile_root": "/tmp/profile"},
        "service_preparation": {"ActiveState": "active"},
        "maintenance_scope": scope.model_dump(mode="json", by_alias=True),
        "maintenance_scope_path": "maintenance/maintenance-scope.json",
        "maintenance_scope_file_sha256": "8" * 64,
        "maintenance_scope_digest": "9" * 64,
        "no_send_oracle": {"schema": "nutricoach-r71b-no-send-oracle-v1"},
        "no_send_oracle_path": "maintenance/no-send-oracle.json",
        "no_send_oracle_file_sha256": "a" * 64,
        "diagnosed_blocker_evidence": [],
    }


def test_two_stage_derivation_replays_without_placeholder() -> None:
    # Given
    scope = _scope()
    inputs = _inputs(scope)

    # When
    first = derive_r71b_package(inputs, scope)
    replay = verify_r71b_package_derivation(
        first.binding_inputs_bytes,
        canonical_document(scope),
        first.authority_bytes,
        first.permission_package_bytes,
    )

    # Then
    assert replay == first
    assert replay.package_binding_digest != replay.final_package_digest
    assert replay.authority.package_binding_digest == replay.package_binding_digest
    assert b"package_digest" not in replay.authority_bytes
    assert b"<pending>" not in replay.authority_bytes


_TAMPERS = (
    "T1-placeholder-field",
    "T2-scope-binding",
    "T3-recomputed-binding",
    "T4-noncanonical-binding",
    "T5-candidate-input",
    "T6-swapped-authority",
    "T7-hold-id",
    "T8-hold-content",
    "T9-authority-hash",
    "T10-final-digest",
    "T11-payload-binding-digest",
    "T12-old-authority-path",
    "T13-missing-input",
    "T14-credential-byte",
    "T15-final-digest-in-audit-shape",
    "T16-mixed-artifacts",
    "T17-second-pass-placeholder",
    "T18-wrong-namespace",
)


@pytest.mark.parametrize("attack", _TAMPERS)
def test_derivation_rejects_each_normative_tamper(attack: str) -> None:
    # Given
    scope = _scope()
    artifacts = derive_r71b_package(_inputs(scope), scope)
    binding = artifacts.binding_inputs_bytes
    scope_bytes = canonical_document(scope)
    authority = artifacts.authority_bytes
    package = artifacts.permission_package_bytes

    # When
    if attack == "T4-noncanonical-binding":
        binding = b" " + binding
    elif attack in {"T6-swapped-authority", "T7-hold-id", "T8-hold-content", "T14-credential-byte"}:
        authority = authority.replace(b'"hold_id":"', b'"hold_id":"0', 1)
    elif attack in {"T9-authority-hash", "T10-final-digest", "T11-payload-binding-digest", "T12-old-authority-path"}:
        package = package.replace(b"r71b", b"r71x", 1)
    elif attack in {"T2-scope-binding", "T15-final-digest-in-audit-shape"}:
        scope_bytes = scope_bytes.replace(b'"card_slot":"5', b'"card_slot":"6', 1)
    else:
        parsed = json.loads(binding)
        if attack == "T13-missing-input":
            del parsed["candidate"]
        elif attack == "T18-wrong-namespace":
            parsed["package_namespace"] = "r72"
        elif attack in {"T1-placeholder-field", "T17-second-pass-placeholder"}:
            parsed["package_digest"] = "<pending>"
        else:
            parsed["candidate"] = {"candidate_digest": "f" * 64}
        binding = canonical_json(parsed) + b"\n"

    # Then
    with pytest.raises(PackageDerivationError):
        verify_r71b_package_derivation(binding, scope_bytes, authority, package)
