"""Covers app/routers/components.py — the reusable Component Library (Page
Designer/Form Designer's "Save as Component" / "My Components" palette
section): round-trip CRUD, per-kind definition validation, the html-block
permission gate on a page_block component, and that a vendor token is
denied outright (Studio-only feature, no Public Portal use case)."""

import uuid

from app.core.auth import create_access_token
from tests.conftest import AsyncSessionLocal
from app.models.identity import Role, User, UserRole


async def _create_role_and_token(tenant_id: uuid.UUID, action_matrix: dict[str, list[str]]) -> str:
    unique = uuid.uuid4().hex[:8]
    async with AsyncSessionLocal() as db:
        async with db.begin():
            role = Role(tenant_id=tenant_id, name=f"Pytest Role {unique}", is_system_role=False, action_matrix=action_matrix)
            db.add(role)
            await db.flush()
            user = User(tenant_id=tenant_id, name=f"Pytest User {unique}", email=f"pytest-{unique}@example.com", status="active")
            db.add(user)
            await db.flush()
            db.add(UserRole(user_id=user.id, role_id=role.id))
            await db.flush()
            user_id = user.id
    return create_access_token(tenant_id=tenant_id, user_id=user_id, vendor_id=None)


def _vendor_headers(tenant_id: uuid.UUID) -> dict[str, str]:
    token = create_access_token(tenant_id=tenant_id, user_id=uuid.uuid4(), vendor_id=uuid.uuid4())
    return {"Authorization": f"Bearer {token}"}


PAGE_BLOCK_DEFINITION = [
    {
        "id": "section-1",
        "type": "section",
        "children": [{"id": "text-1", "type": "text", "text": "Hello"}],
    }
]

FORM_FIELD_DEFINITION = [
    {"name": "street", "type": "text", "label": "Street", "required": True},
    {"name": "city", "type": "text", "label": "City", "required": False},
]


async def test_component_crud_round_trip(client, auth_headers):
    create_resp = await client.post(
        "/components",
        headers=auth_headers,
        json={"kind": "page_block", "name": "Contact Section", "description": "Name + email", "definition": PAGE_BLOCK_DEFINITION},
    )
    assert create_resp.status_code == 201, create_resp.text
    component = create_resp.json()
    assert component["kind"] == "page_block"
    assert component["definition"] == PAGE_BLOCK_DEFINITION

    list_resp = await client.get("/components", headers=auth_headers, params={"kind": "page_block"})
    assert list_resp.status_code == 200
    assert any(c["id"] == component["id"] for c in list_resp.json())

    update_resp = await client.patch(
        f"/components/{component['id']}", headers=auth_headers, json={"name": "Contact Section v2"}
    )
    assert update_resp.status_code == 200
    assert update_resp.json()["name"] == "Contact Section v2"
    # definition is immutable via PATCH — unaffected by the rename
    assert update_resp.json()["definition"] == PAGE_BLOCK_DEFINITION

    delete_resp = await client.delete(f"/components/{component['id']}", headers=auth_headers)
    assert delete_resp.status_code == 204

    after_delete = await client.get("/components", headers=auth_headers, params={"kind": "page_block"})
    assert not any(c["id"] == component["id"] for c in after_delete.json())


async def test_component_form_field_kind_and_filter(client, auth_headers):
    resp = await client.post(
        "/components",
        headers=auth_headers,
        json={"kind": "form_field", "name": "Address Block", "definition": FORM_FIELD_DEFINITION},
    )
    assert resp.status_code == 201, resp.text
    assert len(resp.json()["definition"]) == 2

    page_block_list = await client.get("/components", headers=auth_headers, params={"kind": "page_block"})
    form_field_list = await client.get("/components", headers=auth_headers, params={"kind": "form_field"})
    assert not any(c["name"] == "Address Block" for c in page_block_list.json())
    assert any(c["name"] == "Address Block" for c in form_field_list.json())


async def test_component_page_block_rejects_zero_or_multiple_roots(client, auth_headers):
    empty_resp = await client.post(
        "/components", headers=auth_headers, json={"kind": "page_block", "name": "Empty", "definition": []}
    )
    assert empty_resp.status_code == 422

    multi_resp = await client.post(
        "/components",
        headers=auth_headers,
        json={"kind": "page_block", "name": "Two roots", "definition": PAGE_BLOCK_DEFINITION * 2},
    )
    assert multi_resp.status_code == 422


async def test_component_form_field_rejects_empty_list_and_invalid_field(client, auth_headers):
    empty_resp = await client.post(
        "/components", headers=auth_headers, json={"kind": "form_field", "name": "Empty", "definition": []}
    )
    assert empty_resp.status_code == 422

    invalid_resp = await client.post(
        "/components",
        headers=auth_headers,
        json={"kind": "form_field", "name": "Bad Field", "definition": [{"name": "x", "type": "not_a_real_type", "label": "X"}]},
    )
    assert invalid_resp.status_code == 422


async def test_component_html_block_rejected_without_publish_permission(client, test_tenant):
    token = await _create_role_and_token(test_tenant["tenant_id"], {"pages": ["view", "create", "edit"], "components": ["view", "create"]})
    headers = {"Authorization": f"Bearer {token}"}
    resp = await client.post(
        "/components",
        headers=headers,
        json={
            "kind": "page_block",
            "name": "Html Snippet",
            "definition": [{"id": "h1", "type": "html", "html": "<script>alert(1)</script>"}],
        },
    )
    assert resp.status_code == 403


async def test_component_html_block_allowed_with_publish_permission(client, test_tenant):
    token = await _create_role_and_token(
        test_tenant["tenant_id"], {"pages": ["view", "create", "edit", "publish"], "components": ["view", "create"]}
    )
    headers = {"Authorization": f"Bearer {token}"}
    resp = await client.post(
        "/components",
        headers=headers,
        json={
            "kind": "page_block",
            "name": "Html Snippet 2",
            "definition": [{"id": "h1", "type": "html", "html": "<script>alert(1)</script>"}],
        },
    )
    assert resp.status_code == 201, resp.text


async def test_component_denies_vendor_token(client, test_tenant):
    headers = _vendor_headers(test_tenant["tenant_id"])
    list_resp = await client.get("/components", headers=headers)
    assert list_resp.status_code == 403
    create_resp = await client.post(
        "/components", headers=headers, json={"kind": "form_field", "name": "X", "definition": FORM_FIELD_DEFINITION}
    )
    assert create_resp.status_code == 403
