from __future__ import annotations

import json
from pathlib import Path

import pytest


def test_validate_infer_config_cli_smoke(tmp_path: Path, capsys) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {
                    "name": "vision_patchcore",
                    "device": "cpu",
                    "pretrained": False,
                    "contamination": 0.1,
                    "model_kwargs": {},
                },
                "defects": {
                    "enabled": True,
                    "pixel_threshold_strategy": "normal_pixel_quantile",
                    "pixel_normal_quantile": 0.999,
                    "mask_format": "png",
                },
                "prediction": {
                    "reject_confidence_below": 0.75,
                    "reject_label": -9,
                },
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg)])
    assert rc == 0
    out = capsys.readouterr().out.lower()
    assert "ok" in out


def test_validate_infer_config_cli_rejects_bad_mask_format(tmp_path: Path, capsys) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "defects": {"mask_format": "nope"},
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg)])
    assert rc == 1
    err = capsys.readouterr().err.lower()
    assert "mask_format" in err


def test_validate_infer_config_cli_backfills_legacy_schema_version(tmp_path: Path, capsys) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "defects": {"mask_format": "png"},
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg), "--json"])
    assert rc == 0
    out = json.loads(capsys.readouterr().out)
    assert out["schema_version"] == 1


def test_validate_infer_config_cli_rejects_future_schema_version(tmp_path: Path, capsys) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "schema_version": 999,
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg)])
    assert rc == 1
    err = capsys.readouterr().err.lower()
    assert "schema_version" in err


def test_validate_infer_config_cli_rejects_bad_prediction_threshold(tmp_path: Path, capsys) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "prediction": {"reject_confidence_below": 1.5},
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg)])
    assert rc == 1
    err = capsys.readouterr().err.lower()
    assert "reject_confidence_below" in err


def test_validate_infer_config_cli_rejects_bad_prediction_label(tmp_path: Path, capsys) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "prediction": {
                    "reject_confidence_below": 0.75,
                    "reject_label": "not-an-int",
                },
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg)])
    assert rc == 1
    err = capsys.readouterr().err.lower()
    assert "reject_label" in err


def test_validate_infer_config_cli_rejects_bad_artifact_quality_status(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "artifact_quality": {
                    "status": "unknown",
                    "threshold_scope": "image",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": False,
                    "has_prediction_policy": False,
                    "audit_refs": {"calibration_card": "calibration_card.json"},
                },
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg), "--no-check-files"])
    assert rc == 1
    err = capsys.readouterr().err.lower()
    assert "artifact_quality.status" in err


def test_validate_infer_config_cli_accepts_top_level_postprocess_contract(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "defects": {"mask_format": "png"},
                "postprocess": {
                    "schema_version": 1,
                    "threshold_scope": "image",
                    "image_threshold": {
                        "threshold": 0.42,
                        "score_order": "higher_is_more_anomalous",
                    },
                    "pixel_threshold": {
                        "enabled": True,
                        "strategy": "normal_pixel_quantile",
                        "threshold": None,
                        "normal_quantile": 0.999,
                    },
                    "review_policy": {
                        "review_on": ["anomalous", "rejected_low_confidence"],
                        "confidence_gate_enabled": False,
                        "reject_confidence_below": None,
                        "reject_label": None,
                    },
                    "label_encoding": {"normal": 0, "anomalous": 1},
                },
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg), "--json", "--no-check-files"])
    assert rc == 0
    out = json.loads(capsys.readouterr().out)
    assert out["postprocess"]["image_threshold"]["threshold"] == pytest.approx(0.42)


def test_validate_infer_config_cli_reports_postprocess_contract_trust_signals(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "postprocess": {
                    "schema_version": 1,
                    "threshold_scope": "image",
                    "image_threshold": {
                        "threshold": 0.42,
                        "score_order": "higher_is_more_anomalous",
                    },
                    "review_policy": {
                        "review_on": ["anomalous", "rejected_low_confidence"],
                        "confidence_gate_enabled": True,
                        "reject_confidence_below": 0.8,
                        "reject_label": -5,
                    },
                    "label_encoding": {
                        "normal": 0,
                        "anomalous": 1,
                        "rejected": -5,
                    },
                },
                "artifact_quality": {
                    "status": "audited",
                    "threshold_scope": "image",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": True,
                    "has_prediction_policy": False,
                    "has_postprocess_contract": True,
                    "audit_refs": {"calibration_card": "calibration_card.json"},
                },
            }
        ),
        encoding="utf-8",
    )
    (tmp_path / "calibration_card.json").write_text("{}", encoding="utf-8")

    rc = main([str(cfg), "--json"])

    assert rc == 0
    out = json.loads(capsys.readouterr().out)
    trust = out["validation_trust"]
    assert trust["status"] == "trust-signaled"
    assert trust["trust_signals"]["has_postprocess_contract"] is True
    assert trust["trust_signals"]["has_postprocess_threshold"] is True
    assert trust["trust_signals"]["has_postprocess_review_policy"] is True
    assert trust["trust_signals"]["has_postprocess_label_encoding"] is True

    rc = main([str(cfg)])

    assert rc == 0
    plain = capsys.readouterr().out.lower()
    assert "trust_signal.has_postprocess_contract=true" in plain
    assert "trust_signal.has_postprocess_threshold=true" in plain
    assert "trust_signal.has_postprocess_review_policy=true" in plain
    assert "trust_signal.has_postprocess_label_encoding=true" in plain


def test_validate_infer_config_cli_degrades_incomplete_postprocess_contract(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "postprocess": {
                    "schema_version": 1,
                    "threshold_scope": "image",
                    "image_threshold": {
                        "threshold": 0.42,
                        "score_order": "higher_is_more_anomalous",
                    },
                },
                "artifact_quality": {
                    "status": "audited",
                    "threshold_scope": "image",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": True,
                    "has_prediction_policy": False,
                    "has_postprocess_contract": True,
                    "audit_refs": {"calibration_card": "calibration_card.json"},
                },
            }
        ),
        encoding="utf-8",
    )
    (tmp_path / "calibration_card.json").write_text("{}", encoding="utf-8")

    rc = main([str(cfg), "--json"])

    assert rc == 0
    out = json.loads(capsys.readouterr().out)
    trust = out["validation_trust"]
    assert trust["status"] == "partial"
    assert "missing_postprocess_review_policy" in trust["degraded_by"]
    assert "missing_postprocess_label_encoding" in trust["degraded_by"]


def test_validate_infer_config_cli_rejects_missing_audit_ref_file(tmp_path: Path, capsys) -> None:
    from pyimgano.validate_infer_config_cli import main

    run_dir = tmp_path / "run"
    cfg = run_dir / "artifacts" / "infer_config.json"
    cfg.parent.mkdir(parents=True, exist_ok=True)
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "artifact_quality": {
                    "status": "audited",
                    "threshold_scope": "image",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": True,
                    "has_prediction_policy": False,
                    "audit_refs": {"calibration_card": "artifacts/calibration_card.json"},
                },
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg)])
    assert rc == 1
    err = capsys.readouterr().err.lower()
    assert "artifact_quality.audit_refs.calibration_card" in err


def test_validate_infer_config_cli_rejects_missing_deploy_ref_file(tmp_path: Path, capsys) -> None:
    from pyimgano.validate_infer_config_cli import main

    bundle_dir = tmp_path / "deploy_bundle"
    cfg = bundle_dir / "infer_config.json"
    cfg.parent.mkdir(parents=True, exist_ok=True)
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "artifact_quality": {
                    "status": "deployable",
                    "threshold_scope": "image",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": True,
                    "has_prediction_policy": False,
                    "has_deploy_bundle": True,
                    "has_bundle_manifest": True,
                    "audit_refs": {"calibration_card": "calibration_card.json"},
                    "deploy_refs": {"bundle_manifest": "bundle_manifest.json"},
                },
            }
        ),
        encoding="utf-8",
    )
    (bundle_dir / "calibration_card.json").write_text("{}", encoding="utf-8")

    rc = main([str(cfg)])
    assert rc == 1
    err = capsys.readouterr().err.lower()
    assert "artifact_quality.deploy_refs.bundle_manifest" in err


def test_validate_infer_config_cli_rejects_missing_operator_contract_ref(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "artifact_quality": {
                    "status": "audited",
                    "threshold_scope": "image",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": True,
                    "has_prediction_policy": False,
                    "has_operator_contract": True,
                    "audit_refs": {"calibration_card": "calibration_card.json"},
                },
            }
        ),
        encoding="utf-8",
    )
    (tmp_path / "calibration_card.json").write_text("{}", encoding="utf-8")

    rc = main([str(cfg), "--no-check-files"])
    assert rc == 1
    err = capsys.readouterr().err.lower()
    assert "artifact_quality.audit_refs.operator_contract" in err


def test_validate_infer_config_cli_rejects_missing_operator_contract_payload(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "artifact_quality": {
                    "status": "audited",
                    "threshold_scope": "image",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": True,
                    "has_prediction_policy": False,
                    "has_operator_contract": True,
                    "audit_refs": {
                        "calibration_card": "calibration_card.json",
                        "operator_contract": "operator_contract.json",
                    },
                },
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg), "--no-check-files"])
    assert rc == 1
    err = capsys.readouterr().err.lower()
    assert "has_operator_contract=true requires operator_contract object" in err


def test_validate_infer_config_cli_rejects_operator_contract_prediction_mismatch(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "prediction": {
                    "reject_confidence_below": 0.75,
                    "reject_label": -9,
                },
                "operator_contract": {
                    "schema_version": 1,
                    "review_policy": {
                        "review_on": ["anomalous", "rejected_low_confidence"],
                        "confidence_gate_enabled": True,
                        "reject_confidence_below": 0.6,
                        "reject_label": -9,
                    },
                },
                "artifact_quality": {
                    "status": "audited",
                    "threshold_scope": "image",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": True,
                    "has_prediction_policy": True,
                    "has_operator_contract": True,
                    "audit_refs": {
                        "calibration_card": "calibration_card.json",
                        "operator_contract": "operator_contract.json",
                    },
                },
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg), "--no-check-files"])
    assert rc == 1
    err = capsys.readouterr().err.lower()
    assert "operator_contract.review_policy.reject_confidence_below" in err


def test_validate_infer_config_cli_accepts_consistent_operator_contract(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "defects": {"enabled": True, "mask_format": "png"},
                "prediction": {
                    "reject_confidence_below": 0.75,
                    "reject_label": -9,
                },
                "operator_contract": {
                    "schema_version": 1,
                    "review_policy": {
                        "review_on": ["anomalous", "rejected_low_confidence"],
                        "confidence_gate_enabled": True,
                        "reject_confidence_below": 0.75,
                        "reject_label": -9,
                    },
                    "runtime_policy": {
                        "defects_enabled": True,
                    },
                    "output_contract": {
                        "requires_image_score": True,
                        "supports_pixel_outputs": True,
                        "supports_reject_label": True,
                        "score_order": "higher_is_more_anomalous",
                        "confidence_semantics": "predicted_label_confidence",
                        "confidence_range": [0.0, 1.0],
                        "decision_values": [
                            "score_only",
                            "normal",
                            "anomalous",
                            "rejected_low_confidence",
                        ],
                        "label_encoding": {
                            "normal": 0,
                            "anomalous": 1,
                            "rejected": -9,
                        },
                    },
                },
                "artifact_quality": {
                    "status": "audited",
                    "threshold_scope": "image",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": True,
                    "has_prediction_policy": True,
                    "has_operator_contract": True,
                    "audit_refs": {
                        "calibration_card": "calibration_card.json",
                        "operator_contract": "operator_contract.json",
                    },
                },
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg), "--no-check-files"])
    assert rc == 0
    out = capsys.readouterr().out.lower()
    assert "trust_signal.has_operator_contract=true" in out
    assert "trust_signal.has_operator_contract_ref=true" in out


def test_validate_infer_config_cli_rejects_output_contract_rejected_label_mismatch(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "prediction": {
                    "reject_confidence_below": 0.75,
                    "reject_label": -9,
                },
                "operator_contract": {
                    "schema_version": 1,
                    "review_policy": {
                        "review_on": ["anomalous", "rejected_low_confidence"],
                        "confidence_gate_enabled": True,
                        "reject_confidence_below": 0.75,
                        "reject_label": -9,
                    },
                    "output_contract": {
                        "requires_image_score": True,
                        "supports_pixel_outputs": False,
                        "supports_reject_label": True,
                        "score_order": "higher_is_more_anomalous",
                        "confidence_semantics": "predicted_label_confidence",
                        "confidence_range": [0.0, 1.0],
                        "decision_values": [
                            "score_only",
                            "normal",
                            "anomalous",
                            "rejected_low_confidence",
                        ],
                        "label_encoding": {
                            "normal": 0,
                            "anomalous": 1,
                            "rejected": -7,
                        },
                    },
                },
            }
        ),
        encoding="utf-8",
    )

    rc = main([str(cfg), "--no-check-files"])
    assert rc == 1
    err = capsys.readouterr().err.lower()
    assert "label_encoding.rejected" in err


def test_validate_infer_config_cli_plain_output_shows_artifact_quality(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    cfg = tmp_path / "infer_config.json"
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "artifact_quality": {
                    "status": "audited",
                    "threshold_scope": "image",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": False,
                    "has_prediction_policy": False,
                    "has_deploy_bundle": False,
                    "has_bundle_manifest": False,
                    "audit_refs": {"calibration_card": "calibration_card.json"},
                    "deploy_refs": {},
                },
            }
        ),
        encoding="utf-8",
    )
    (tmp_path / "calibration_card.json").write_text("{}", encoding="utf-8")

    rc = main([str(cfg)])

    assert rc == 0
    out = capsys.readouterr().out.lower()
    assert "audit_status=audited" in out
    assert "threshold_scope=image" in out
    assert "deploy_bundle=false" in out
    assert "bundle_manifest=false" in out
    assert "trust_status=partial" in out
    assert "degraded_by=missing_split_fingerprint" in out
    assert "audit_ref.calibration_card=calibration_card.json" in out


def test_validate_infer_config_cli_reports_bundle_completeness_metadata(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    bundle_dir = tmp_path / "deploy_bundle"
    cfg = bundle_dir / "infer_config.json"
    cfg.parent.mkdir(parents=True, exist_ok=True)
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "artifact_quality": {
                    "status": "deployable",
                    "threshold_scope": "image",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": True,
                    "has_prediction_policy": False,
                    "has_deploy_bundle": True,
                    "has_bundle_manifest": True,
                    "required_bundle_artifacts_present": True,
                    "bundle_artifact_roles": {
                        "infer_config": ["infer_config.json"],
                        "report": ["report.json"],
                        "config": ["config.json"],
                        "environment": ["environment.json"],
                    },
                    "audit_refs": {"calibration_card": "calibration_card.json"},
                    "deploy_refs": {"bundle_manifest": "bundle_manifest.json"},
                },
            }
        ),
        encoding="utf-8",
    )
    (bundle_dir / "calibration_card.json").write_text("{}", encoding="utf-8")
    (bundle_dir / "bundle_manifest.json").write_text("{}", encoding="utf-8")

    rc = main([str(cfg), "--json"])

    assert rc == 0
    out = json.loads(capsys.readouterr().out)
    trust = out["validation_trust"]
    assert trust["trust_signals"]["has_required_bundle_artifacts"] is True
    assert trust["trust_signals"]["has_bundle_artifact_roles"] is True

    rc = main([str(cfg)])

    assert rc == 0
    plain = capsys.readouterr().out.lower()
    assert "bundle_required=true" in plain
    assert "trust_signal.file_refs_checked=true" in plain
    assert "trust_signal.has_required_bundle_artifacts=true" in plain
    assert "trust_signal.has_bundle_artifact_roles=true" in plain


def test_validate_infer_config_cli_accepts_pixel_threshold_scope_for_bundle_contract(
    tmp_path: Path, capsys
) -> None:
    from pyimgano.validate_infer_config_cli import main

    bundle_dir = tmp_path / "deploy_bundle"
    cfg = bundle_dir / "infer_config.json"
    cfg.parent.mkdir(parents=True, exist_ok=True)
    cfg.write_text(
        json.dumps(
            {
                "model": {"name": "vision_patchcore", "model_kwargs": {}},
                "defects": {"pixel_threshold": 0.5, "mask_format": "png"},
                "artifact_quality": {
                    "status": "deployable",
                    "threshold_scope": "pixel",
                    "has_threshold_provenance": True,
                    "has_split_fingerprint": True,
                    "has_prediction_policy": False,
                    "has_deploy_bundle": True,
                    "has_bundle_manifest": True,
                    "required_bundle_artifacts_present": True,
                    "bundle_artifact_roles": {
                        "infer_config": ["infer_config.json"],
                        "report": ["report.json"],
                        "config": ["config.json"],
                        "environment": ["environment.json"],
                    },
                    "audit_refs": {"calibration_card": "calibration_card.json"},
                    "deploy_refs": {"bundle_manifest": "bundle_manifest.json"},
                },
            }
        ),
        encoding="utf-8",
    )
    (bundle_dir / "calibration_card.json").write_text("{}", encoding="utf-8")
    (bundle_dir / "bundle_manifest.json").write_text("{}", encoding="utf-8")

    rc = main([str(cfg), "--json"])

    assert rc == 0
    out = json.loads(capsys.readouterr().out)
    assert out["artifact_quality"]["threshold_scope"] == "pixel"
