"""
T2.1: Nested Workflow Execution

Tests that parent workflows can call child workflows, creating a proper
execution hierarchy with correct parent-child relationships.

Test validates:
- Multi-level execution hierarchy (parent → child → grandchildren)
- parent_execution_id chains are correct
- Execution tree structure is maintained
- Results propagate up from children to parent
- Parent waits for all descendants to complete
"""

import time

import pytest
from helpers.client import AttuneClient
from helpers.fixtures import create_echo_action, unique_ref
from helpers.polling import (
    wait_for_execution_count,
    wait_for_execution_status,
)


def test_nested_workflow_execution(client: AttuneClient, test_pack):
    """
    Test that workflows can call child workflows, creating proper execution hierarchy.

    Execution tree:
        Parent Workflow (execution_id=1)
        └─ Child Workflow (execution_id=2, parent=1)
           ├─ Task 1 (execution_id=3, parent=2)
           └─ Task 2 (execution_id=4, parent=2)
    """
    print("\n" + "=" * 80)
    print("TEST: Nested Workflow Execution (T2.1)")
    print("=" * 80)

    pack_ref = test_pack["ref"]

    # ========================================================================
    # STEP 1: Create child actions that will be called by child workflow
    # ========================================================================
    print("\n[STEP 1] Creating child actions...")

    task1_action = create_echo_action(
        client=client,
        pack_ref=pack_ref,
        action_name=f"task1_{unique_ref()}",
        echo_message="Task 1 executed",
    )
    print(f"✓ Created task1 action: {task1_action['ref']}")

    task2_action = create_echo_action(
        client=client,
        pack_ref=pack_ref,
        action_name=f"task2_{unique_ref()}",
        echo_message="Task 2 executed",
    )
    print(f"✓ Created task2 action: {task2_action['ref']}")

    # ========================================================================
    # STEP 2: Create child workflow action (calls task1 and task2)
    # ========================================================================
    print("\n[STEP 2] Creating child workflow action...")

    child_workflow_action = client.create_action(
        pack_ref=pack_ref,
        data={
            "name": f"child_workflow_{unique_ref()}",
            "description": "Child workflow with 2 tasks",
            "runner_type": "workflow",
            "entry_point": "",
            "enabled": True,
            "parameters": {},
            "workflow_definition": {
                "tasks": [
                    {
                        "name": "child_task_1",
                        "action": task1_action["ref"],
                        "parameters": {},
                    },
                    {
                        "name": "child_task_2",
                        "action": task2_action["ref"],
                        "parameters": {},
                    },
                ]
            },
        },
    )
    child_workflow_ref = child_workflow_action["ref"]
    print(f"✓ Created child workflow: {child_workflow_ref}")
    print(f"  - Tasks: child_task_1, child_task_2")

    # ========================================================================
    # STEP 3: Create parent workflow action (calls child workflow)
    # ========================================================================
    print("\n[STEP 3] Creating parent workflow action...")

    parent_workflow_action = client.create_action(
        pack_ref=pack_ref,
        data={
            "name": f"parent_workflow_{unique_ref()}",
            "description": "Parent workflow that calls child workflow",
            "runner_type": "workflow",
            "entry_point": "",
            "enabled": True,
            "parameters": {},
            "workflow_definition": {
                "tasks": [
                    {
                        "name": "call_child_workflow",
                        "action": child_workflow_ref,
                        "parameters": {},
                    }
                ]
            },
        },
    )
    parent_workflow_ref = parent_workflow_action["ref"]
    print(f"✓ Created parent workflow: {parent_workflow_ref}")
    print(f"  - Calls: {child_workflow_ref}")

    # ========================================================================
    # STEP 4: Execute parent workflow
    # ========================================================================
    print("\n[STEP 4] Executing parent workflow...")

    parent_execution = client.create_execution(
        action_ref=parent_workflow_ref, parameters={}
    )
    parent_execution_id = parent_execution["id"]
    print(f"✓ Parent execution created: ID={parent_execution_id}")

    # ========================================================================
    # STEP 5: Wait for parent to complete
    # ========================================================================
    print("\n[STEP 5] Waiting for parent workflow to complete...")

    parent_result = wait_for_execution_status(
        client=client,
        execution_id=parent_execution_id,
        expected_status="succeeded",
        timeout=30,
    )
    print(f"✓ Parent workflow completed: status={parent_result['status']}")

    # ========================================================================
    # STEP 6: Verify execution hierarchy
    # ========================================================================
    print("\n[STEP 6] Verifying execution hierarchy...")

    # Get all executions for this test
    all_executions = client.list_executions(limit=100)

    # Filter to our executions (parent and children)
    our_executions = [
        ex
        for ex in all_executions
        if ex["id"] == parent_execution_id
        or ex.get("parent_execution_id") == parent_execution_id
    ]

    print(f"  Found {len(our_executions)} total executions")

    # Build execution tree
    parent_exec = None
    child_workflow_exec = None
    grandchild_execs = []

    for ex in our_executions:
        if ex["id"] == parent_execution_id:
            parent_exec = ex
        elif ex.get("parent_execution_id") == parent_execution_id:
            # This is the child workflow execution
            child_workflow_exec = ex

    assert parent_exec is not None, "Parent execution not found"
    assert child_workflow_exec is not None, "Child workflow execution not found"

    print(f"\n  Execution Tree:")
    print(f"  └─ Parent (ID={parent_exec['id']}, status={parent_exec['status']})")
    print(
        f"     └─ Child Workflow (ID={child_workflow_exec['id']}, parent={child_workflow_exec.get('parent_execution_id')}, status={child_workflow_exec['status']})"
    )

    # Find grandchildren (task executions under child workflow)
    child_workflow_id = child_workflow_exec["id"]
    grandchild_execs = [
        ex
        for ex in all_executions
        if ex.get("parent_execution_id") == child_workflow_id
    ]

    print(f"     Found {len(grandchild_execs)} grandchild executions:")
    for gc in grandchild_execs:
        print(
            f"        └─ Task (ID={gc['id']}, parent={gc.get('parent_execution_id')}, action={gc['action_ref']}, status={gc['status']})"
        )

    # ========================================================================
    # STEP 7: Validate success criteria
    # ========================================================================
    print("\n[STEP 7] Validating success criteria...")

    # Criterion 1: At least 3 execution levels exist
    assert parent_exec is not None, "❌ Parent execution missing"
    assert child_workflow_exec is not None, "❌ Child workflow execution missing"
    assert len(grandchild_execs) >= 2, (
        f"❌ Expected at least 2 grandchild executions, got {len(grandchild_execs)}"
    )
    print("  ✓ 3 execution levels exist: parent → child → grandchildren")

    # Criterion 2: parent_execution_id chain is correct
    assert child_workflow_exec["parent_execution_id"] == parent_execution_id, (
        f"❌ Child workflow parent_id incorrect: expected {parent_execution_id}, got {child_workflow_exec['parent_execution_id']}"
    )
    print(f"  ✓ Child workflow parent_execution_id = {parent_execution_id}")

    for gc in grandchild_execs:
        assert gc["parent_execution_id"] == child_workflow_id, (
            f"❌ Grandchild parent_id incorrect: expected {child_workflow_id}, got {gc['parent_execution_id']}"
        )
    print(f"  ✓ All grandchildren have parent_execution_id = {child_workflow_id}")

    # Criterion 3: All executions completed successfully
    assert parent_exec["status"] == "succeeded", (
        f"❌ Parent status not succeeded: {parent_exec['status']}"
    )
    assert child_workflow_exec["status"] == "succeeded", (
        f"❌ Child workflow status not succeeded: {child_workflow_exec['status']}"
    )

    for gc in grandchild_execs:
        assert gc["status"] == "succeeded", (
            f"❌ Grandchild {gc['id']} status not succeeded: {gc['status']}"
        )
    print("  ✓ All executions completed successfully")

    # Criterion 4: Verify execution tree structure
    # Parent should have started first, then child, then grandchildren
    parent_start = parent_exec.get("start_timestamp")
    child_start = child_workflow_exec.get("start_timestamp")

    if parent_start and child_start:
        assert child_start >= parent_start, "❌ Child started before parent"
        print(f"  ✓ Execution order correct: parent started before child")

    # Criterion 5: Verify all task executions reference correct actions
    task_refs = {gc["action_ref"] for gc in grandchild_execs}
    expected_refs = {task1_action["ref"], task2_action["ref"]}

    assert task_refs == expected_refs, (
        f"❌ Task action refs don't match: expected {expected_refs}, got {task_refs}"
    )
    print(f"  ✓ All task actions executed correctly")

    # ========================================================================
    # FINAL SUMMARY
    # ========================================================================
    print("\n" + "=" * 80)
    print("TEST SUMMARY: Nested Workflow Execution")
    print("=" * 80)
    print(f"✓ Parent workflow executed: {parent_workflow_ref}")
    print(f"✓ Child workflow executed: {child_workflow_ref}")
    print(f"✓ Execution hierarchy validated:")
    print(f"  - Parent execution ID: {parent_execution_id}")
    print(f"  - Child workflow execution ID: {child_workflow_id}")
    print(f"  - Grandchild executions: {len(grandchild_execs)}")
    print(f"✓ All {1 + 1 + len(grandchild_execs)} executions succeeded")
    print(f"✓ parent_execution_id chains correct")
    print(f"✓ Execution tree structure maintained")
    print("\n✅ TEST PASSED: Nested workflow execution works correctly!")
    print("=" * 80 + "\n")


def test_deeply_nested_workflow(client: AttuneClient, test_pack):
    """
    Test deeper nesting: 3 levels of workflows (great-grandchildren).

    Execution tree:
        Level 0: Root Workflow
        └─ Level 1: Child Workflow
           └─ Level 2: Grandchild Workflow
              └─ Level 3: Task Action
    """
    print("\n" + "=" * 80)
    print("TEST: Deeply Nested Workflow (3 Levels)")
    print("=" * 80)

    pack_ref = test_pack["ref"]

    # ========================================================================
    # STEP 1: Create leaf action (level 3)
    # ========================================================================
    print("\n[STEP 1] Creating leaf action...")

    leaf_action = create_echo_action(
        client=client,
        pack_ref=pack_ref,
        action_name=f"leaf_{unique_ref()}",
        echo_message="Leaf action at level 3",
    )
    print(f"✓ Created leaf action: {leaf_action['ref']}")

    # ========================================================================
    # STEP 2: Create grandchild workflow (level 2)
    # ========================================================================
    print("\n[STEP 2] Creating grandchild workflow (level 2)...")

    grandchild_workflow = client.create_action(
        pack_ref=pack_ref,
        data={
            "name": f"grandchild_wf_{unique_ref()}",
            "description": "Grandchild workflow (level 2)",
            "runner_type": "workflow",
            "entry_point": "",
            "enabled": True,
            "parameters": {},
            "workflow_definition": {
                "tasks": [
                    {
                        "name": "call_leaf",
                        "action": leaf_action["ref"],
                        "parameters": {},
                    }
                ]
            },
        },
    )
    print(f"✓ Created grandchild workflow: {grandchild_workflow['ref']}")

    # ========================================================================
    # STEP 3: Create child workflow (level 1)
    # ========================================================================
    print("\n[STEP 3] Creating child workflow (level 1)...")

    child_workflow = client.create_action(
        pack_ref=pack_ref,
        data={
            "name": f"child_wf_{unique_ref()}",
            "description": "Child workflow (level 1)",
            "runner_type": "workflow",
            "entry_point": "",
            "enabled": True,
            "parameters": {},
            "workflow_definition": {
                "tasks": [
                    {
                        "name": "call_grandchild",
                        "action": grandchild_workflow["ref"],
                        "parameters": {},
                    }
                ]
            },
        },
    )
    print(f"✓ Created child workflow: {child_workflow['ref']}")

    # ========================================================================
    # STEP 4: Create root workflow (level 0)
    # ========================================================================
    print("\n[STEP 4] Creating root workflow (level 0)...")

    root_workflow = client.create_action(
        pack_ref=pack_ref,
        data={
            "name": f"root_wf_{unique_ref()}",
            "description": "Root workflow (level 0)",
            "runner_type": "workflow",
            "entry_point": "",
            "enabled": True,
            "parameters": {},
            "workflow_definition": {
                "tasks": [
                    {
                        "name": "call_child",
                        "action": child_workflow["ref"],
                        "parameters": {},
                    }
                ]
            },
        },
    )
    print(f"✓ Created root workflow: {root_workflow['ref']}")

    # ========================================================================
    # STEP 5: Execute root workflow
    # ========================================================================
    print("\n[STEP 5] Executing root workflow...")

    root_execution = client.create_execution(
        action_ref=root_workflow["ref"], parameters={}
    )
    root_execution_id = root_execution["id"]
    print(f"✓ Root execution created: ID={root_execution_id}")

    # ========================================================================
    # STEP 6: Wait for completion
    # ========================================================================
    print("\n[STEP 6] Waiting for all nested workflows to complete...")

    root_result = wait_for_execution_status(
        client=client,
        execution_id=root_execution_id,
        expected_status="succeeded",
        timeout=40,
    )
    print(f"✓ Root workflow completed: status={root_result['status']}")

    # ========================================================================
    # STEP 7: Verify 4-level hierarchy
    # ========================================================================
    print("\n[STEP 7] Verifying 4-level execution hierarchy...")

    all_executions = client.list_executions(limit=100)

    # Build hierarchy by following parent_execution_id chain
    def find_children(parent_id):
        return [
            ex for ex in all_executions if ex.get("parent_execution_id") == parent_id
        ]

    level0 = [ex for ex in all_executions if ex["id"] == root_execution_id][0]
    level1 = find_children(level0["id"])
    level2 = []
    for l1 in level1:
        level2.extend(find_children(l1["id"]))
    level3 = []
    for l2 in level2:
        level3.extend(find_children(l2["id"]))

    print(f"\n  Execution Hierarchy:")
    print(f"  Level 0 (Root):       {len([level0])} execution")
    print(f"  Level 1 (Child):      {len(level1)} execution(s)")
    print(f"  Level 2 (Grandchild): {len(level2)} execution(s)")
    print(f"  Level 3 (Leaf):       {len(level3)} execution(s)")

    # ========================================================================
    # STEP 8: Validate success criteria
    # ========================================================================
    print("\n[STEP 8] Validating success criteria...")

    assert len(level1) >= 1, (
        f"❌ Expected at least 1 level 1 execution, got {len(level1)}"
    )
    assert len(level2) >= 1, (
        f"❌ Expected at least 1 level 2 execution, got {len(level2)}"
    )
    assert len(level3) >= 1, (
        f"❌ Expected at least 1 level 3 execution, got {len(level3)}"
    )
    print("  ✓ All 4 execution levels present")

    # Verify all succeeded
    all_execs = [level0] + level1 + level2 + level3
    for ex in all_execs:
        assert ex["status"] == "succeeded", (
            f"❌ Execution {ex['id']} failed: {ex['status']}"
        )
    print(f"  ✓ All {len(all_execs)} executions succeeded")

    # ========================================================================
    # FINAL SUMMARY
    # ========================================================================
    print("\n" + "=" * 80)
    print("TEST SUMMARY: Deeply Nested Workflow (3 Levels)")
    print("=" * 80)
    print(f"✓ 4-level execution hierarchy created:")
    print(f"  - Root workflow (level 0)")
    print(f"  - Child workflow (level 1)")
    print(f"  - Grandchild workflow (level 2)")
    print(f"  - Leaf action (level 3)")
    print(f"✓ Total executions: {len(all_execs)}")
    print(f"✓ All executions succeeded")
    print(f"✓ parent_execution_id chain validated")
    print("\n✅ TEST PASSED: Deep nesting works correctly!")
    print("=" * 80 + "\n")
