"""Template sets (plan §6, manager hat): fill once -> a punchlist's worth of
items, each pinning its own template and taking only the variables it declares."""

from sqlalchemy import select

from app.db import get_session_factory
from app.models import Item
from app.services import punchlists as pls
from app.services import sets as sets_svc
from tests.conftest import OWNER, as_user

import pytest


@pytest.fixture
def session():
    with get_session_factory()() as s:
        yield s
        s.commit()


def test_seeded_set_exists_and_references_resolve(session):
    tset = sets_svc.latest_published(session, "website_onboarding")
    assert tset is not None and len(tset.items) == 16


def test_publish_refuses_unknown_templates(session):
    with pytest.raises(sets_svc.SetError) as e:
        sets_svc.publish(session, OWNER, {
            "key": "bad", "title": "Bad",
            "items": [{"template_key": "ga4_admin_access"},
                      {"template_key": "does_not_exist"}]})
    assert "does_not_exist" in str(e.value)


def test_instantiate_fills_once_and_filters_variables(session):
    pl = pls.create(session, OWNER, "Set test")
    run, created = sets_svc.instantiate(
        session, OWNER, pl.id, "website_onboarding",
        {"who_needs_access": ["a@x.com", "b@x.com"]})
    assert len(created) == 16
    # order preserved: first item is the deposit invoice, last is content
    assert created[0].title == "Pay the deposit invoice"
    assert created[-1].title == "Send us photos and content"
    # variable filtering: ga4 got the emails; deposit_invoice got none
    ga4 = next(i for i in created if i.title.startswith("Grant GA4"))
    assert ga4.variables == {"who_needs_access": ["a@x.com", "b@x.com"]}
    assert created[0].variables == {}
    # all linked to the run, positions strictly increasing
    rows = session.execute(select(Item).where(Item.set_run_id == run.id)
                           .order_by(Item.position)).scalars().all()
    assert len(rows) == 16
    assert [r.position for r in rows] == sorted(r.position for r in rows)


def test_instantiate_requires_shared_variables(session):
    pl = pls.create(session, OWNER, "Set test 2")
    with pytest.raises(sets_svc.SetError) as e:
        sets_svc.instantiate(session, OWNER, pl.id, "website_onboarding", {})
    assert e.value.code == "MISSING_VARIABLES"


def test_api_run_set_needs_compose(client, kit):
    as_user(client, OWNER)
    r = client.post("/api/punchlists", json={"title": "API set test"})
    pid = r.json()["id"]
    kit.member("bob", "member")
    kit.grant("bob", pid, "member")
    as_user(client, "bob")
    r = client.post(f"/api/punchlists/{pid}/set-runs",
                    json={"set_key": "website_onboarding",
                          "variables": {"who_needs_access": ["a@x.com"]}})
    assert r.status_code == 403

    as_user(client, OWNER)
    r = client.post(f"/api/punchlists/{pid}/set-runs",
                    json={"set_key": "website_onboarding",
                          "variables": {"who_needs_access": ["a@x.com"]}})
    assert r.status_code == 201
    assert len(r.json()["items"]) == 16
    # labels rendered on every returned item (no raw {{tokens}})
    assert all("{{" not in (i["label"] or "") for i in r.json()["items"])
