"""A datasource "reference" field can link to a Form's submissions instead of
another datasource's records (MasterFieldDef.reference_form_id): the stored
value is a submission id, checked on write, shown as the linked form field's
value in queries, and it protects the Form from deletion."""


async def _form_with_submissions(client, headers):
    form = await client.post(
        "/forms",
        headers=headers,
        json={"name": "Applicants", "field_definitions": [{"name": "full_name", "type": "text", "label": "Full name"}]},
    )
    assert form.status_code == 201, form.text
    form_id = form.json()["form_id"]
    published = await client.post(f"/forms/{form_id}/publish", headers=headers)
    assert published.status_code == 200, published.text
    version = published.json()["version"]
    ids = []
    for name in ("Asha Nair", "Ben Thomas"):
        sub = await client.post("/form-submissions", headers=headers, json={"form_id": form_id, "form_version": version, "field_values": {"full_name": name}})
        assert sub.status_code == 201, sub.text
        ids.append(sub.json()["id"])
    return form_id, ids


def _case_schema(form_id):
    return [
        {"name": "title", "type": "varchar"},
        {"name": "applicant", "type": "reference", "reference_form_id": form_id, "reference_display_field": "full_name"},
    ]


async def test_reference_to_form_validates_and_resolves_names(client, auth_headers):
    form_id, ids = await _form_with_submissions(client, auth_headers)
    master = await client.post("/masters", headers=auth_headers, json={"name": "Cases (form link)", "field_schema": _case_schema(form_id)})
    assert master.status_code == 201, master.text
    field = next(f for f in master.json()["field_schema"] if f["name"] == "applicant")
    assert field["reference_form_id"] == form_id and field.get("reference_master_id") is None

    ok = await client.post(f"/masters/{master.json()['id']}/records", headers=auth_headers, json={"field_values": {"title": "T1", "applicant": ids[0]}})
    assert ok.status_code == 201, ok.text

    # an id that isn't a submission of that form is refused
    bad = await client.post(
        f"/masters/{master.json()['id']}/records",
        headers=auth_headers,
        json={"field_values": {"title": "T2", "applicant": "11111111-1111-1111-1111-111111111111"}},
    )
    assert bad.status_code == 422 and "linked form" in str(bad.json()["detail"])

    # queries show the submission's display field, not its id
    q = await client.post("/datasets/query", headers=auth_headers, json={"source_type": "master", "source_id": master.json()["id"], "columns": ["title", "applicant"], "filters": []})
    assert q.status_code == 200, q.text
    assert [r["applicant"] for r in q.json()["rows"]] == ["Asha Nair"]


async def test_reference_needs_exactly_one_target(client, auth_headers):
    form_id, _ = await _form_with_submissions(client, auth_headers)
    other = await client.post("/masters", headers=auth_headers, json={"name": "Some datasource", "field_schema": [{"name": "n", "type": "varchar"}]})
    both = await client.post(
        "/masters",
        headers=auth_headers,
        json={"name": "Both", "field_schema": [{"name": "x", "type": "reference", "reference_master_id": other.json()["id"], "reference_form_id": form_id, "reference_display_field": "n"}]},
    )
    assert both.status_code == 422
    neither = await client.post("/masters", headers=auth_headers, json={"name": "Neither", "field_schema": [{"name": "x", "type": "reference", "reference_display_field": "n"}]})
    assert neither.status_code == 422


async def test_scope_field_cannot_link_to_a_form(client, auth_headers):
    form_id, _ = await _form_with_submissions(client, auth_headers)
    resp = await client.post("/masters", headers=auth_headers, json={"name": "Scoped by form", "field_schema": _case_schema(form_id), "scope_field": "applicant"})
    assert resp.status_code == 422


async def test_linked_form_cannot_be_deleted(client, auth_headers):
    # an unpublished form is otherwise deletable, so the link is what blocks it
    form = await client.post("/forms", headers=auth_headers, json={"name": "Draft applicants", "field_definitions": [{"name": "full_name", "type": "text", "label": "Full name"}]})
    form_id = form.json()["form_id"]
    await client.post("/masters", headers=auth_headers, json={"name": "Links a draft form", "field_schema": _case_schema(form_id)})
    resp = await client.delete(f"/forms/{form_id}", headers=auth_headers)
    assert resp.status_code == 409 and "linked from datasource" in resp.json()["detail"]
