"""Tests for complyform.core.attestation.builder and canonical modules."""

from __future__ import annotations

import base64
import json

from complyform.core.attestation.builder import build_attestation
from complyform.core.attestation.canonical import canonical_json


def _make_assessment_json() -> dict:
    return {
        "schema_version": 1,
        "tool": "complyform",
        "tool_version": "0.1.0",
        "command": "assess",
        "timestamp": "2025-01-01T00:00:00+00:00",
        "cloud": "gcp",
        "scan_source": "state",
        "state_hash": "sha256:abc123",
        "frameworks": ["soc2"],
        "summary": {
            "total_controls": 10,
            "passed": 7,
            "failed": 2,
            "manual_review": 1,
            "not_assessed": 0,
            "score": 77.8,
            "severity_counts": {"MEDIUM": 8, "HIGH": 2},
        },
        "findings": [],
        "metadata": {
            "resource_count": 5,
            "assessment_duration_ms": 150,
            "tier": "community",
            "cache_hit": True,
            "provider_versions": {"google": "5.0.0"},
        },
        "cloud_intelligence": None,
        "upgrade_hints": None,
    }


class TestInTotoStatement:
    def test_produces_valid_in_toto_statement_v1(self) -> None:
        assessment = _make_assessment_json()
        envelope = build_attestation(assessment, "./assessment.json")

        # Decode the payload
        payload_bytes = base64.b64decode(envelope["payload"])
        statement = json.loads(payload_bytes)

        assert statement["_type"] == "https://in-toto.io/Statement/v1"
        assert "subject" in statement
        assert len(statement["subject"]) == 1
        assert statement["subject"][0]["name"] == "./assessment.json"
        assert "sha256" in statement["subject"][0]["digest"]
        assert (
            statement["predicateType"]
            == "https://complyform.dev/attestation/assessment/v1"
        )
        assert "predicate" in statement


class TestDSSEEnvelope:
    def test_has_payload_type_and_payload(self) -> None:
        assessment = _make_assessment_json()
        envelope = build_attestation(assessment, "./assessment.json")
        assert envelope["payloadType"] == "application/vnd.in-toto+json"
        assert "payload" in envelope

    def test_signatures_is_empty_list(self) -> None:
        assessment = _make_assessment_json()
        envelope = build_attestation(assessment, "./assessment.json")
        assert "signatures" in envelope
        assert envelope["signatures"] == []


class TestBase64PayloadDecodes:
    def test_payload_decodes_to_json_with_type_field(self) -> None:
        assessment = _make_assessment_json()
        envelope = build_attestation(assessment, "./assessment.json")
        payload_bytes = base64.b64decode(envelope["payload"])
        statement = json.loads(payload_bytes)
        assert "_type" in statement


class TestCanonicalJson:
    def test_sorts_keys(self) -> None:
        obj = {"z": 1, "a": 2, "m": 3}
        result = canonical_json(obj)
        decoded = result.decode("utf-8")
        # Keys should appear in sorted order
        keys = list(json.loads(decoded).keys())
        assert keys == ["a", "m", "z"]

    def test_no_whitespace(self) -> None:
        obj = {"key": "value", "nested": {"inner": 1}}
        result = canonical_json(obj)
        decoded = result.decode("utf-8")
        assert " " not in decoded
        assert "\n" not in decoded
        assert "\t" not in decoded

    def test_returns_bytes(self) -> None:
        obj = {"key": "value"}
        result = canonical_json(obj)
        assert isinstance(result, bytes)

    def test_deterministic(self) -> None:
        obj = {"b": 2, "a": 1}
        assert canonical_json(obj) == canonical_json(obj)
