"""CPU-only CLI and independence-contract tests for OID v2 parents."""
from __future__ import annotations

import hashlib
import json
from pathlib import Path

import pytest

from CCD_analysis.utils.run_contract import ContractError
from OID_analysis.v2.parent import (
    _distinctness_diagnostics,
    build_parser,
    validate_parent_request,
)


def _manifest(realization_id: str, token: str) -> dict:
    digest = hashlib.sha256(token.encode()).hexdigest()
    return {
        "realization_id": realization_id,
        "bundle_sha256": digest,
        "trajectory_sha256": hashlib.sha256(f"trajectory-{token}".encode()).hexdigest(),
        "pre_bias_ddf_sha256": hashlib.sha256(f"state-{token}".encode()).hexdigest(),
    }


def test_parent_cli_requires_explicit_realization_and_offset():
    parser = build_parser()
    common = ["--scene", "karman_re100", "--device", "2", "--count", "8",
              "--output-dir", "parent", "--norm", "norm.json"]
    with pytest.raises(SystemExit):
        parser.parse_args(common)
    args = parser.parse_args(common + ["--realization-id", "r2",
                                      "--warmup-offset-windows", "17"])
    assert args.realization_id == "r2"
    assert args.warmup_offset_windows == 17
    assert args.device == 2


def test_parent_cli_supports_both_formal_scenes_and_only_physical_gpu_two():
    parser = build_parser()
    illusion = parser.parse_args([
        "--scene", "illusion_1.0L", "--realization-id", "r3",
        "--warmup-offset-windows", "29", "--device", "2", "--count", "10",
        "--output-dir", "parent", "--norm", "norm.json", "--harmonics", "harmonics.json",
        "--compare-parent", "r1", "--compare-parent", "r2",
    ])
    assert illusion.scene == "illusion_1.0L"
    assert illusion.compare_parent == [Path("r1"), Path("r2")]
    with pytest.raises(SystemExit):
        parser.parse_args([
            "--scene", "karman_re100", "--realization-id", "r2",
            "--warmup-offset-windows", "17", "--device", "0", "--count", "8",
            "--output-dir", "parent", "--norm", "norm.json",
        ])


def test_scene_specific_parent_inputs_fail_closed():
    validate_parent_request(scene="karman_re100", realization_id="r2", count=5,
                            warmup_offset_windows=17, harmonics_path=None)
    validate_parent_request(scene="illusion_1.0L", realization_id="r2", count=5,
                            warmup_offset_windows=17, harmonics_path=Path("harmonics.json"))
    with pytest.raises(ContractError, match="requires --harmonics"):
        validate_parent_request(scene="illusion_1.0L", realization_id="r2", count=5,
                                warmup_offset_windows=17, harmonics_path=None)
    with pytest.raises(ContractError, match="does not use --harmonics"):
        validate_parent_request(scene="karman_re100", realization_id="r2", count=5,
                                warmup_offset_windows=17, harmonics_path=Path("harmonics.json"))
    with pytest.raises(ContractError, match="non-negative"):
        validate_parent_request(scene="karman_re100", realization_id="r2", count=5,
                                warmup_offset_windows=-1, harmonics_path=None)


def test_distinctness_diagnostics_hashes_manifest_and_accepts_distinct_parent(tmp_path):
    existing_dir = tmp_path / "r1"
    existing_dir.mkdir()
    existing_path = existing_dir / "parent_manifest.json"
    existing_path.write_text(json.dumps(_manifest("r1", "one")), encoding="utf-8")
    diagnostics = _distinctness_diagnostics(_manifest("r2", "two"), [existing_dir])
    assert len(diagnostics) == 1
    assert diagnostics[0]["checkpoint_distinct"] is True
    assert diagnostics[0]["trajectory_distinct"] is True
    assert diagnostics[0]["pre_bias_state_distinct"] is True
    assert diagnostics[0]["compared_parent_manifest_sha256"] == hashlib.sha256(
        existing_path.read_bytes()).hexdigest()


def test_distinctness_diagnostics_rejects_duplicate_checkpoint(tmp_path):
    existing = _manifest("r1", "same")
    path = tmp_path / "parent_manifest.json"
    path.write_text(json.dumps(existing), encoding="utf-8")
    candidate = _manifest("r2", "other")
    candidate["bundle_sha256"] = existing["bundle_sha256"]
    with pytest.raises(ContractError, match="checkpoint_distinct"):
        _distinctness_diagnostics(candidate, [path])
