"""DGR-026 — exact split-GGUF artifact manifest.

Deterministic, offline, GPU-free, and download-free: every manifest here is a
tiny in-memory fixture, never a real model artifact.
"""

from __future__ import annotations

import copy
import hashlib

import pytest

from meshnet_node.split_gguf.manifest import (
    SplitArtifactManifestError,
    SplitFile,
    canonical_sha256,
    parse_split_artifact_manifest,
)


def _sha(label: str) -> str:
    return hashlib.sha256(label.encode()).hexdigest()


def _rev(label: str) -> str:
    # a syntactically valid 40-hex "revision" derived from a human label
    return hashlib.sha1(label.encode()).hexdigest()


def _manifest_doc() -> dict:
    return {
        "schema_version": 1,
        "manifest_id": "deepseek-v4-flash-q4_k_m-2way",
        "manifest_version": "2026-07-22.1",
        "quantization": "Q4_K_M",
        "source": {
            "artifact_id": "deepseek-v4-flash",
            "repo_id": "example/deepseek-v4-flash-gguf",
            "revision": _rev("source-revision"),
            "sha256": _sha("whole-model-artifact"),
            "size_bytes": 2000,
        },
        "tokenizer": {
            "repo_id": "example/deepseek-v4-flash",
            "revision": _rev("tokenizer-revision"),
            "sha256": _sha("tokenizer-bytes"),
        },
        "total_bytes": 1200,
        "splits": [
            {
                "name": "model-00001-of-00002.gguf",
                "size_bytes": 700,
                "sha256": _sha("split-1"),
                "role": "layers-0-20",
                "url": "https://example.invalid/model-00001-of-00002.gguf",
                "shard_start": 0,
                "shard_end": 20,
            },
            {
                "name": "model-00002-of-00002.gguf",
                "size_bytes": 500,
                "sha256": _sha("split-2"),
                "role": "layers-20-43",
                "url": "https://example.invalid/model-00002-of-00002.gguf",
                "shard_start": 20,
                "shard_end": 43,
            },
        ],
    }


@pytest.fixture
def manifest_doc() -> dict:
    return _manifest_doc()


@pytest.fixture
def manifest(manifest_doc):
    return parse_split_artifact_manifest(manifest_doc, origin="<fixture>")


def test_manifest_resolves_source_tokenizer_and_both_splits(manifest):
    assert manifest.source.artifact_id == "deepseek-v4-flash"
    assert manifest.source.sha256 == _sha("whole-model-artifact")
    assert manifest.tokenizer.repo_id == "example/deepseek-v4-flash"
    assert [s.name for s in manifest.splits] == [
        "model-00001-of-00002.gguf",
        "model-00002-of-00002.gguf",
    ]
    assert manifest.split("model-00001-of-00002.gguf").role == "layers-0-20"
    assert manifest.split("model-00001-of-00002.gguf").shard_start == 0
    assert manifest.split("model-00001-of-00002.gguf").shard_end == 20


def test_manifest_aggregate_bytes_are_exact_and_self_consistent(manifest):
    assert manifest.total_bytes == sum(s.size_bytes for s in manifest.splits) == 1200


def test_quantization_and_topology_are_manifest_data_not_constants(manifest_doc):
    # A manifest with a different quantization label and a different split
    # count must parse just as validly — nothing in this module hardcodes
    # either.
    doc = copy.deepcopy(manifest_doc)
    doc["quantization"] = "IQ2_XS"
    doc["manifest_id"] = "single-split-example"
    doc["total_bytes"] = 2000
    doc["splits"] = [
        {
            "name": "model-00001-of-00001.gguf",
            "size_bytes": 2000,
            "sha256": _sha("single-split"),
            "role": "whole",
        }
    ]
    manifest = parse_split_artifact_manifest(doc, origin="<fixture>")
    assert manifest.quantization == "IQ2_XS"
    assert len(manifest.splits) == 1
    assert manifest.splits[0].has_range is False


def test_manifest_digest_is_stable_canonical_json(manifest, manifest_doc):
    assert manifest.digest == canonical_sha256(manifest_doc)


def test_split_with_only_shard_start_is_rejected(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    del doc["splits"][0]["shard_end"]
    with pytest.raises(SplitArtifactManifestError, match="both shard_start and shard_end"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_split_with_empty_range_is_rejected(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    doc["splits"][0]["shard_end"] = doc["splits"][0]["shard_start"]
    with pytest.raises(SplitArtifactManifestError, match="shard_end"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_a_missing_split_field_is_rejected(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    del doc["splits"][0]["role"]
    with pytest.raises(SplitArtifactManifestError, match="role"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_a_duplicate_split_name_is_rejected(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    doc["splits"][1]["name"] = doc["splits"][0]["name"]
    with pytest.raises(SplitArtifactManifestError, match="duplicate split name"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_two_splits_claiming_the_same_content_digest_are_rejected(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    doc["splits"][1]["sha256"] = doc["splits"][0]["sha256"]
    with pytest.raises(SplitArtifactManifestError, match="repeats SHA-256"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_an_inconsistent_aggregate_byte_total_is_rejected(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    doc["total_bytes"] = 999999
    with pytest.raises(SplitArtifactManifestError, match="does not equal the sum"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_a_split_size_edited_to_make_the_artifact_look_smaller_is_rejected(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    doc["splits"][0]["size_bytes"] = 1
    with pytest.raises(SplitArtifactManifestError, match="does not equal the sum"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_a_truncated_sha256_is_rejected(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    doc["splits"][0]["sha256"] = doc["splits"][0]["sha256"][:10]
    with pytest.raises(SplitArtifactManifestError, match="64-character hex SHA-256"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_a_branch_name_is_not_an_acceptable_source_revision_pin(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    doc["source"]["revision"] = "main"
    with pytest.raises(SplitArtifactManifestError, match="40-character commit revision"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_a_branch_name_is_not_an_acceptable_tokenizer_revision_pin(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    doc["tokenizer"]["revision"] = "main"
    with pytest.raises(SplitArtifactManifestError, match="40-character commit revision"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_an_unsupported_schema_version_is_rejected(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    doc["schema_version"] = 2
    with pytest.raises(SplitArtifactManifestError, match="schema version"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_an_empty_splits_array_is_rejected(manifest_doc):
    doc = copy.deepcopy(manifest_doc)
    doc["splits"] = []
    with pytest.raises(SplitArtifactManifestError, match="non-empty JSON array"):
        parse_split_artifact_manifest(doc, origin="<fixture>")


def test_split_file_is_a_plain_frozen_dataclass_round_trip():
    split = SplitFile(
        name="a.gguf", size_bytes=10, sha256=_sha("a"), role="whole", url="file:///a.gguf"
    )
    assert split.has_range is False
    assert split.to_dict()["name"] == "a.gguf"
    assert "shard_start" not in split.to_dict()
