from copy import deepcopy

import pytest

from m3m_disease.dataset import (
    assess_model_promotion,
    assert_no_group_leakage,
    assign_splits,
    group_key,
    split_by_group,
    split_receipt,
    validate_records,
)


def test_group_split_is_deterministic_and_uses_field_flight_date(trained_bundle):
    records = trained_bundle["records"]
    first = split_by_group(records, seed=7)
    second = split_by_group(records, seed=7)
    assert first == second
    key = group_key(records[0])
    assert key == "|".join([records[0]["field_id"], records[0]["flight_id"], records[0]["capture_date"]])
    assigned = assign_splits(records, first)
    assert_no_group_leakage(assigned)


def test_leakage_detector_rejects_one_group_in_two_splits(trained_bundle):
    rows = [deepcopy(trained_bundle["assigned"][0]), deepcopy(trained_bundle["assigned"][1])]
    rows[1]["group_key"] = rows[0]["group_key"]
    rows[1]["split"] = "test" if rows[0]["split"] != "test" else "train"
    with pytest.raises(ValueError, match="leakage"):
        assert_no_group_leakage(rows)


def test_duplicate_sample_id_is_rejected(trained_bundle):
    rows = [deepcopy(trained_bundle["records"][0]), deepcopy(trained_bundle["records"][1])]
    rows[1]["sample_id"] = rows[0]["sample_id"]
    with pytest.raises(ValueError, match="Duplicate"):
        validate_records(rows)


def test_split_receipt_has_all_partitions_and_no_leakage(trained_bundle):
    assignments = split_by_group(trained_bundle["records"], seed=20260804)
    receipt = split_receipt(trained_bundle["records"], assignments, seed=20260804)
    assert receipt["leakageDetected"] is False
    assert set(receipt["counts"]) == {"train", "validation", "test"}
    assert len(receipt["assignmentSha256"]) == 64


def test_synthetic_data_forces_research_only(trained_bundle):
    result = assess_model_promotion(trained_bundle["assigned"])
    assert result["status"] == "RESEARCH_ONLY"
    assert "non_field_or_synthetic_data_present" in result["failures"]


def test_cross_field_lab_truth_gate_can_only_reach_field_validation_candidate(trained_bundle):
    template = trained_bundle["assigned"][0]
    rows = []
    for split, field, count in (("train", "FIELD-A", 60), ("test", "FIELD-B", 60)):
        for index in range(count):
            row = deepcopy(template)
            row.update({
                "sample_id": f"{field}-{index:03d}",
                "field_id": field,
                "flight_id": f"{field}-FL01",
                "capture_date": "2026-08-04" if split == "train" else "2026-08-05",
                "data_provenance": "field",
                "label": index % 2,
                "label_reference": "laboratory_confirmed",
                "second_coder_id": "CODER-B",
                "adjudication_status": "resolved",
                "group_key": f"{field}|{field}-FL01|" + ("2026-08-04" if split == "train" else "2026-08-05"),
                "split": split,
            })
            rows.append(row)
    result = assess_model_promotion(rows)
    assert result["status"] == "FIELD_VALIDATION_CANDIDATE"
    assert result["boundary"].startswith("This gate does not establish")
