"""Session data computed at sign-in (Users → Session data after login):
user fields, datasource lookups (chained via "session.<key>"), and an
optional task whose outputs join too. See app/crud/login_session.py."""

from tests.conftest import AsyncSessionLocal
from app.models.identity import User


async def _login(client, test_tenant):
    resp = await client.post(
        "/auth/login",
        json={"tenant_id": str(test_tenant["tenant_id"]), "email": test_tenant["admin_email"], "password": test_tenant["admin_password"]},
    )
    assert resp.status_code == 200, resp.text
    return resp.json()


async def _master(client, auth_headers, name, fields, rows):
    master = (await client.post("/masters", headers=auth_headers, json={"name": name, "field_schema": [{"name": f, "type": "text"} for f in fields]})).json()
    ids = []
    for row in rows:
        r = await client.post(f"/masters/{master['id']}/records", headers=auth_headers, json={"field_values": row})
        assert r.status_code == 201, r.text
        ids.append(r.json()["id"])
    return master["id"], ids


async def test_no_settings_means_empty_session_data(client, test_tenant):
    assert (await _login(client, test_tenant))["session_data"] == {}


async def test_user_fields_and_chained_lookups(client, test_tenant, auth_headers):
    async with AsyncSessionLocal() as db:
        async with db.begin():
            user = await db.get(User, test_tenant["admin_id"])
            user.extra_fields = {"employee_code": "E-42"}

    staff_id, _ = await _master(client, auth_headers, "Staff", ["email", "office_id"], [{"email": test_tenant["admin_email"], "office_id": "OFF-7"}, {"email": "x@example.com", "office_id": "OFF-1"}])
    offices_id, office_ids = await _master(client, auth_headers, "Offices", ["code", "region"], [{"code": "OFF-7", "region": "South"}])

    settings = {
        "values": [
            {"key": "employee_code", "source": "user", "user_field": "employee_code"},
            {"key": "office_id", "source": "datasource", "source_id": staff_id, "match_field": "email", "match_on": "email", "value_field": "office_id"},
            {"key": "region", "source": "datasource", "source_id": offices_id, "match_field": "code", "match_on": "session.office_id", "value_field": "region"},
            {"key": "office_record", "source": "datasource", "source_id": offices_id, "match_field": "code", "match_on": "session.office_id", "value_field": "id"},
            {"key": "missing", "source": "datasource", "source_id": staff_id, "match_field": "email", "match_on": "phone", "value_field": "office_id"},
        ]
    }
    saved = await client.patch("/tenants/me/login-session", headers=auth_headers, json=settings)
    assert saved.status_code == 200, saved.text

    expected = {"employee_code": "E-42", "office_id": "OFF-7", "region": "South", "office_record": office_ids[0], "missing": None}
    assert (await _login(client, test_tenant))["session_data"] == expected

    preview = await client.post("/tenants/me/login-session/preview", headers=auth_headers, json=settings)
    assert preview.status_code == 200 and preview.json()["session_data"] == expected


async def test_settings_validation(client, auth_headers):
    bad_order = {"values": [{"key": "region", "source": "datasource", "source_id": "00000000-0000-0000-0000-000000000001", "match_field": "code", "match_on": "session.office_id", "value_field": "region"}]}
    assert (await client.patch("/tenants/me/login-session", headers=auth_headers, json=bad_order)).status_code == 422

    dup = {"values": [{"key": "a", "source": "user", "user_field": "email"}, {"key": "a", "source": "user", "user_field": "name"}]}
    assert (await client.patch("/tenants/me/login-session", headers=auth_headers, json=dup)).status_code == 422

    unknown_source = {"values": [{"key": "a", "source": "datasource", "source_id": "00000000-0000-0000-0000-000000000001", "match_field": "x", "match_on": "id", "value_field": "y"}]}
    resp = await client.patch("/tenants/me/login-session", headers=auth_headers, json=unknown_source)
    assert resp.status_code == 422 and "no longer exists" in resp.text


async def test_task_outputs_join_session_data(client, test_tenant, auth_headers):
    offices_id, _ = await _master(client, auth_headers, "Offices", ["code", "region"], [{"code": "OFF-7", "region": "South"}])
    task = (
        await client.post(
            "/tasks",
            headers=auth_headers,
            json={
                "name": "Office region",
                "nodes": [
                    {"key": "start", "type": "start", "label": "Start", "next_nodes": [{"to": "get"}]},
                    {
                        "key": "get",
                        "type": "data_get",
                        "label": "Get office",
                        "master_id": offices_id,
                        "match_field": "code",
                        "input_params": [{"name": "match_value", "type": "string", "source_field": "session.office_code"}],
                        "output_params": [{"name": "region", "type": "string"}],
                    },
                ],
            },
        )
    ).json()
    settings = {"values": [{"key": "office_code", "source": "user", "user_field": "name"}], "task_id": task["id"]}

    draft = await client.patch("/tenants/me/login-session", headers=auth_headers, json=settings)
    assert draft.status_code == 422 and "Publish the task" in draft.text

    assert (await client.post(f"/tasks/{task['id']}/publish", headers=auth_headers)).status_code == 200
    async with AsyncSessionLocal() as db:
        async with db.begin():
            (await db.get(User, test_tenant["admin_id"])).name = "OFF-7"
    assert (await client.patch("/tenants/me/login-session", headers=auth_headers, json=settings)).status_code == 200

    data = (await _login(client, test_tenant))["session_data"]
    assert data == {"office_code": "OFF-7", "region": "South"}
