"""POST /tasks/{id}/duplicate and POST /workflows/{id}/duplicate — a new
independent draft copied from the source (app/routers/tasks.py,
app/routers/workflows.py)."""

import uuid

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

TASK_NODES = [
    {"key": "start", "type": "start", "label": "Start", "next_nodes": [{"to": "check"}]},
    {"key": "check", "type": "condition", "label": "Has email?", "x": 240, "y": 80},
]

WORKFLOW_STEPS = [
    {"key": "start", "type": "start", "label": "Start", "next_steps": [{"to": "review"}]},
    {"key": "review", "type": "approval", "label": "Review", "sla_hours": 24, "next_steps": [{"to": "end"}]},
    {"key": "end", "type": "end", "label": "End"},
]


async def _token(tenant_id: uuid.UUID, action_matrix: dict[str, list[str]]) -> dict[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))
            user_id = user.id
    return {"Authorization": f"Bearer {create_access_token(tenant_id=tenant_id, user_id=user_id, vendor_id=None)}"}


async def test_duplicate_task_copies_nodes_as_new_draft(client, auth_headers):
    src = (await client.post("/tasks", headers=auth_headers, json={"name": "Notify approver", "nodes": TASK_NODES})).json()
    await client.post(f"/tasks/{src['id']}/publish", headers=auth_headers)

    resp = await client.post(f"/tasks/{src['id']}/duplicate", headers=auth_headers, json={})
    assert resp.status_code == 201, resp.text
    dup = resp.json()
    assert dup["id"] != src["id"]
    assert dup["name"] == "Notify approver (copy)"
    assert dup["status"] == "draft"
    assert dup["nodes"] == src["nodes"]

    named = await client.post(f"/tasks/{src['id']}/duplicate", headers=auth_headers, json={"name": "  Notify v2 "})
    assert named.json()["name"] == "Notify v2"

    # Independent: editing the copy leaves the source alone.
    await client.patch(f"/tasks/{dup['id']}", headers=auth_headers, json={"nodes": []})
    source_after = (await client.get(f"/tasks/{src['id']}", headers=auth_headers)).json()
    assert source_after["nodes"] == src["nodes"]
    assert source_after["status"] == "published"


async def test_duplicate_task_needs_create_permission(client, auth_headers, test_tenant):
    src = (await client.post("/tasks", headers=auth_headers, json={"name": "Guarded", "nodes": TASK_NODES})).json()
    viewer = await _token(test_tenant["tenant_id"], {"tasks": ["view", "edit"]})
    assert (await client.post(f"/tasks/{src['id']}/duplicate", headers=viewer, json={})).status_code == 403
    assert (await client.post(f"/tasks/{uuid.uuid4()}/duplicate", headers=auth_headers, json={})).status_code == 404


async def test_duplicate_workflow_is_a_new_workflow_with_step_forms(client, auth_headers):
    form = (
        await client.post("/forms", headers=auth_headers, json={"name": "Review form", "field_definitions": [{"name": "note", "type": "text", "label": "Note"}]})
    ).json()
    src = (await client.post("/workflows", headers=auth_headers, json={"name": "PO Approval", "step_definitions": WORKFLOW_STEPS})).json()
    wid = src["workflow_id"]
    link = await client.put(
        f"/workflows/{wid}/versions/1/steps/review", headers=auth_headers, json={"step_key": "review", "form_id": form["form_id"], "form_version": form["version"]}
    )
    assert link.status_code == 200, link.text
    await client.post(f"/workflows/{wid}/publish", headers=auth_headers)
    # Duplicate from the current version, which is now v2.
    v2 = (await client.post(f"/workflows/{wid}/new-version", headers=auth_headers)).json()
    assert v2["version"] == 2

    resp = await client.post(f"/workflows/{wid}/duplicate", headers=auth_headers, json={})
    assert resp.status_code == 201, resp.text
    dup = resp.json()
    assert dup["workflow_id"] != wid
    assert dup["version"] == 1
    assert dup["status"] == "draft"
    assert dup["name"] == "PO Approval (copy)"
    assert dup["step_definitions"] == v2["step_definitions"]

    # v2 has no form links of its own (new-version doesn't copy them), so none come across.
    steps = (await client.get(f"/workflows/{dup['workflow_id']}/versions/1/steps", headers=auth_headers)).json()
    assert steps == []

    # Duplicating a version that has links brings them along.
    src2 = (await client.post("/workflows", headers=auth_headers, json={"name": "Leave", "step_definitions": WORKFLOW_STEPS})).json()
    await client.put(
        f"/workflows/{src2['workflow_id']}/versions/1/steps/review",
        headers=auth_headers,
        json={"step_key": "review", "form_id": form["form_id"], "form_version": form["version"]},
    )
    dup2 = (await client.post(f"/workflows/{src2['workflow_id']}/duplicate", headers=auth_headers, json={"name": "Leave v2"})).json()
    assert dup2["name"] == "Leave v2"
    steps2 = (await client.get(f"/workflows/{dup2['workflow_id']}/versions/1/steps", headers=auth_headers)).json()
    assert [(s["step_key"], s["form_id"]) for s in steps2] == [("review", form["form_id"])]

    # The copy is its own workflow: both are listed, and the source's history is untouched.
    listed = {w["workflow_id"] for w in (await client.get("/workflows", headers=auth_headers)).json()}
    assert {wid, dup["workflow_id"]} <= listed
    assert len((await client.get(f"/workflows/{wid}/versions", headers=auth_headers)).json()) == 2
    assert len((await client.get(f"/workflows/{dup['workflow_id']}/versions", headers=auth_headers)).json()) == 1


async def test_duplicate_workflow_needs_create_permission(client, auth_headers, test_tenant):
    src = (await client.post("/workflows", headers=auth_headers, json={"name": "Guarded", "step_definitions": WORKFLOW_STEPS})).json()
    editor = await _token(test_tenant["tenant_id"], {"workflows": ["view", "edit"]})
    assert (await client.post(f"/workflows/{src['workflow_id']}/duplicate", headers=editor, json={})).status_code == 403
    assert (await client.post(f"/workflows/{uuid.uuid4()}/duplicate", headers=auth_headers, json={})).status_code == 404
