import json

from recoil.pipeline._lib import validation
from recoil.pipeline._lib.validation import Validator, ValidationResult


GATE_0_KEYS = [
    "identity_consistent",
    "wardrobe_consistent",
    "no_artifacts",
    "clean_background",
]

GATE_1_IMAGE_KEYS = [
    "black_frame",
    "watermark",
    "anatomy",
    "color",
    "resolution",
]


def _dummy_file(tmp_path, name):
    path = tmp_path / name
    path.write_bytes(b"dummy")
    return path


def _check_payload(keys, pass_value, reason="x"):
    return {key: {"pass": pass_value, "reason": reason} for key in keys}


def _run_gate_0(monkeypatch, tmp_path, raw):
    monkeypatch.setattr(Validator, "_call_flash", lambda self, *args, **kwargs: raw)
    ref1 = _dummy_file(tmp_path, "ref1.png")
    ref2 = _dummy_file(tmp_path, "ref2.png")
    result = Validator().run_gate_0("jade", [ref1, ref2])
    assert isinstance(result, ValidationResult)
    return result


def _run_gate_1_image(monkeypatch, tmp_path, raw):
    monkeypatch.setattr(Validator, "_call_flash", lambda self, *args, **kwargs: raw)
    img = _dummy_file(tmp_path, "image.png")
    result = Validator().run_gate_1_image(img)
    assert isinstance(result, ValidationResult)
    return result


def test_gate_0_flags_parseable_degenerate_json(tmp_path, monkeypatch):
    result = _run_gate_0(monkeypatch, tmp_path, "{}")
    assert result.passed is False
    assert result.details["flagged_for_review"] is True
    assert result.details["raw"] == {}

    result = _run_gate_0(monkeypatch, tmp_path, "[]")
    assert result.passed is False
    assert result.details["flagged_for_review"] is True

    result = _run_gate_0(monkeypatch, tmp_path, '{"identity_consistent": true}')
    assert result.passed is False

    result = _run_gate_0(monkeypatch, tmp_path, '{"foo": {"bar": 1}}')
    assert result.passed is False


def test_gate_0_rejects_non_boolean_truthy_pass_values(tmp_path, monkeypatch):
    result = _run_gate_0(
        monkeypatch,
        tmp_path,
        json.dumps(_check_payload(GATE_0_KEYS, "false")),
    )
    assert result.passed is False
    assert result.details["flagged_for_review"] is True

    result = _run_gate_0(
        monkeypatch,
        tmp_path,
        json.dumps(_check_payload(GATE_0_KEYS, 1)),
    )
    assert result.passed is False
    assert result.details["flagged_for_review"] is True


def test_gate_0_preserves_valid_boolean_verdicts(tmp_path, monkeypatch):
    result = _run_gate_0(
        monkeypatch,
        tmp_path,
        json.dumps(_check_payload(GATE_0_KEYS, True, reason="ok")),
    )
    assert result.passed is True

    payload = _check_payload(GATE_0_KEYS, True, reason="ok")
    payload["no_artifacts"]["pass"] = False
    result = _run_gate_0(monkeypatch, tmp_path, json.dumps(payload))
    assert result.passed is False
    assert not result.details.get("flagged_for_review")


def test_gate_1_image_flags_parseable_degenerate_json(tmp_path, monkeypatch):
    result = _run_gate_1_image(monkeypatch, tmp_path, "{}")
    assert result.passed is False
    assert result.details["flagged_for_review"] is True
    assert result.details["raw"] == {}

    result = _run_gate_1_image(monkeypatch, tmp_path, "null")
    assert result.passed is False
    assert result.details["flagged_for_review"] is True

    result = _run_gate_1_image(monkeypatch, tmp_path, '{"black_frame": true}')
    assert result.passed is False


def test_gate_1_image_rejects_non_boolean_truthy_pass_values(tmp_path, monkeypatch):
    result = _run_gate_1_image(
        monkeypatch,
        tmp_path,
        json.dumps(_check_payload(GATE_1_IMAGE_KEYS, 1)),
    )
    assert result.passed is False
    assert result.details["flagged_for_review"] is True

    result = _run_gate_1_image(
        monkeypatch,
        tmp_path,
        json.dumps(_check_payload(GATE_1_IMAGE_KEYS, "false")),
    )
    assert result.passed is False
    assert result.details["flagged_for_review"] is True


def test_gate_1_image_preserves_valid_boolean_verdicts(tmp_path, monkeypatch):
    result = _run_gate_1_image(
        monkeypatch,
        tmp_path,
        json.dumps(_check_payload(GATE_1_IMAGE_KEYS, True, reason="ok")),
    )
    assert result.passed is True

    payload = _check_payload(GATE_1_IMAGE_KEYS, True, reason="ok")
    payload["black_frame"]["pass"] = False
    result = _run_gate_1_image(monkeypatch, tmp_path, json.dumps(payload))
    assert result.passed is False


def test_gate_3_frame_extraction_failure_deferred_and_flagged(tmp_path, monkeypatch):
    video = _dummy_file(tmp_path, "clip.mp4")
    ref = _dummy_file(tmp_path, "ref.png")
    monkeypatch.setattr(validation, "_extract_frames_at_pcts", lambda *args, **kwargs: [None])

    result = Validator().run_gate_3(video, [ref])

    assert result.passed is False
    assert result.details["deferred"] is True
    assert result.details["flagged_for_review"] is True
