"""Form.prefill (FormPrefillConfig): fetches one record from a Master or
another Form's submissions, filtered by a URL query parameter, and copies
its fields into this Form's own fields on load — see
frontend/src/lib/useFormPrefill.ts. The backend's job is just to store/
validate the config; the actual fetch reuses the already-tested
POST /datasets/query."""


async def _country_master(client, headers):
    resp = await client.post(
        "/masters",
        headers=headers,
        json={"name": "Country", "field_schema": [{"name": "code", "type": "text"}, {"name": "name", "type": "text"}]},
    )
    assert resp.status_code == 201, resp.text
    return resp.json()["id"]


def _prefill(master_id, *, field_mappings=None):
    return {
        "source_type": "master",
        "source_id": master_id,
        "match_field": "code",
        "param_name": "countryCode",
        "field_mappings": field_mappings if field_mappings is not None else [{"source_field": "name", "form_field": "country_name"}],
    }


def _fields():
    return [{"name": "country_name", "type": "text", "label": "Country Name"}]


async def test_create_form_with_prefill_round_trips(client, auth_headers):
    master_id = await _country_master(client, auth_headers)
    resp = await client.post(
        "/forms",
        headers=auth_headers,
        json={"name": "Prefill test", "field_definitions": _fields(), "prefill": _prefill(master_id)},
    )
    assert resp.status_code == 201, resp.text
    body = resp.json()
    assert body["prefill"]["source_type"] == "master"
    assert body["prefill"]["source_id"] == master_id
    assert body["prefill"]["match_field"] == "code"
    assert body["prefill"]["param_name"] == "countryCode"
    assert body["prefill"]["field_mappings"] == [{"source_field": "name", "form_field": "country_name"}]

    fetched = await client.get(f"/forms/{body['form_id']}", headers=auth_headers)
    assert fetched.status_code == 200
    assert fetched.json()["prefill"] == body["prefill"]


async def test_create_form_prefill_needs_at_least_one_mapping(client, auth_headers):
    master_id = await _country_master(client, auth_headers)
    resp = await client.post(
        "/forms",
        headers=auth_headers,
        json={"name": "Empty mapping", "field_definitions": _fields(), "prefill": _prefill(master_id, field_mappings=[])},
    )
    assert resp.status_code == 422


async def test_create_form_prefill_mapping_must_target_a_real_field(client, auth_headers):
    master_id = await _country_master(client, auth_headers)
    bad_mapping = [{"source_field": "name", "form_field": "no_such_field"}]
    resp = await client.post(
        "/forms",
        headers=auth_headers,
        json={"name": "Bad target", "field_definitions": _fields(), "prefill": _prefill(master_id, field_mappings=bad_mapping)},
    )
    assert resp.status_code == 422
    assert "no_such_field" in str(resp.json()["detail"])


async def test_form_without_prefill_defaults_to_null(client, auth_headers):
    resp = await client.post("/forms", headers=auth_headers, json={"name": "No prefill", "field_definitions": _fields()})
    assert resp.status_code == 201, resp.text
    assert resp.json()["prefill"] is None


async def test_patch_can_add_and_clear_prefill(client, auth_headers):
    master_id = await _country_master(client, auth_headers)
    created = await client.post("/forms", headers=auth_headers, json={"name": "Patchable", "field_definitions": _fields()})
    form_id = created.json()["form_id"]
    assert created.json()["prefill"] is None

    added = await client.patch(f"/forms/{form_id}", headers=auth_headers, json={"prefill": _prefill(master_id), "field_definitions": _fields()})
    assert added.status_code == 200, added.text
    assert added.json()["prefill"]["match_field"] == "code"

    # Patching an unrelated field (name only) must NOT touch the already-set prefill.
    renamed = await client.patch(f"/forms/{form_id}", headers=auth_headers, json={"name": "Patchable (renamed)"})
    assert renamed.status_code == 200, renamed.text
    assert renamed.json()["prefill"]["match_field"] == "code"

    # Explicitly sending prefill: null clears it.
    cleared = await client.patch(f"/forms/{form_id}", headers=auth_headers, json={"prefill": None})
    assert cleared.status_code == 200, cleared.text
    assert cleared.json()["prefill"] is None


async def test_new_version_carries_prefill_forward(client, auth_headers):
    master_id = await _country_master(client, auth_headers)
    created = await client.post(
        "/forms", headers=auth_headers, json={"name": "Versioned", "field_definitions": _fields(), "prefill": _prefill(master_id)}
    )
    form_id = created.json()["form_id"]
    await client.post(f"/forms/{form_id}/publish", headers=auth_headers)

    new_version = await client.post(f"/forms/{form_id}/new-version", headers=auth_headers)
    assert new_version.status_code == 201, new_version.text
    assert new_version.json()["prefill"]["match_field"] == "code"
    assert new_version.json()["prefill"]["field_mappings"] == [{"source_field": "name", "form_field": "country_name"}]


async def test_context_mappings_without_a_lookup(client, auth_headers):
    prefill = {
        "source_id": "",
        "context_mappings": [
            {"context_key": "countryCode", "form_field": "country_name"},
            {"context_key": "session.office_id", "form_field": "country_name"},
        ],
    }
    resp = await client.post("/forms", headers=auth_headers, json={"name": "Direct", "field_definitions": _fields(), "prefill": prefill})
    assert resp.status_code == 201, resp.text
    saved = resp.json()["prefill"]
    assert saved["source_id"] is None and saved["field_mappings"] == []
    assert saved["context_mappings"][1] == {"context_key": "session.office_id", "form_field": "country_name"}


async def test_lookup_can_match_on_a_session_key_alongside_context_mappings(client, auth_headers):
    master_id = await _country_master(client, auth_headers)
    prefill = {**_prefill(master_id), "param_name": "session.country", "context_mappings": [{"context_key": "current_user.name", "form_field": "country_name"}]}
    resp = await client.post("/forms", headers=auth_headers, json={"name": "Both", "field_definitions": _fields(), "prefill": prefill})
    assert resp.status_code == 201, resp.text
    assert resp.json()["prefill"]["param_name"] == "session.country"


async def test_field_default_value_round_trips(client, auth_headers):
    fields = [{"name": "office", "type": "text", "label": "Office {{session.office_id}}", "default_value": "{{session.office_id}}"}]
    resp = await client.post("/forms", headers=auth_headers, json={"name": "Defaults", "field_definitions": fields})
    assert resp.status_code == 201, resp.text
    saved = resp.json()["field_definitions"][0]
    assert saved["default_value"] == "{{session.office_id}}" and saved["label"] == "Office {{session.office_id}}"


async def test_prefill_validation(client, auth_headers):
    master_id = await _country_master(client, auth_headers)
    cases = [
        {"context_mappings": []},  # nothing mapped
        {"context_mappings": [{"context_key": " ", "form_field": "country_name"}]},  # blank key
        {"context_mappings": [{"context_key": "x", "form_field": "nope"}]},  # unknown target
        {"field_mappings": [{"source_field": "name", "form_field": "country_name"}]},  # lookup mappings, no source
        {**_prefill(master_id), "param_name": ""},  # lookup with nothing to match on
    ]
    for prefill in cases:
        resp = await client.post("/forms", headers=auth_headers, json={"name": "Bad", "field_definitions": _fields(), "prefill": prefill})
        assert resp.status_code == 422, (prefill, resp.text)
