import asyncio
import json
from agentui.pipeline.executor import PipelineExecutor, ExecutionError, Node, NODE_REGISTRY
from tests.utils import pp as _pp, base_ctx as _base_ctx



async def scenario_if_single_quotes_ok():
    print("\n=== SCENARIO 1: If with single quotes ===")
    p = {
        "id": "p_if_single_quotes",
        "name": "If Single Quotes",
        "loop_mode": "iterative",
        "nodes": [
            {
                "id": "n1",
                "type": "SetVars",
                "config": {
                    "variables": [
                        {"id": "v1", "name": "MSG", "mode": "string", "value": "Hello"}
                    ]
                },
                "in": {}
            },
            {
                "id": "nIf",
                "type": "If",
                "config": {
                    "expr": "[[MSG]] contains 'Hello'"
                },
                "in": {
                    "depends": "n1.done"
                }
            },
            {
                "id": "nRet",
                "type": "Return",
                "config": {
                    "target_format": "openai",
                    "text_template": "ok"
                },
                "in": {
                    "depends": "nIf.true"
                }
            }
        ]
    }
    exe = PipelineExecutor(p)
    try:
        out = await exe.run(_base_ctx())
        print("OK:", _pp(out))
    except Exception as e:
        print("ERR:", type(e).__name__, str(e))

async def scenario_if_error_logging():
    print("\n=== SCENARIO 2: If with unterminated string (expect error log) ===")
    p = {
        "id": "p_if_bad_string",
        "name": "If Bad String",
        "loop_mode": "iterative",
        "nodes": [
            {
                "id": "n1",
                "type": "SetVars",
                "config": {
                    "variables": [
                        {"id": "v1", "name": "MSG", "mode": "string", "value": "Hello"}
                    ]
                },
                "in": {}
            },
            {
                "id": "nIf",
                "type": "If",
                "config": {
                    # Missing closing quote to force tokenizer error
                    "expr": "[[MSG]] contains 'Hello"
                },
                "in": {
                    "depends": "n1.done"
                }
            },
            {
                "id": "nRet",
                "type": "Return",
                "config": {
                    "target_format": "openai",
                    "text_template": "should_not_run"
                },
                "in": {
                    "depends": "nIf.true"
                }
            }
        ]
    }
    exe = PipelineExecutor(p)
    try:
        out = await exe.run(_base_ctx())
        print("UNEXPECTED_OK:", _pp(out))
    except Exception as e:
        print("EXPECTED_ERROR:", type(e).__name__, str(e))

async def scenario_multi_depends_array():
    print("\n=== SCENARIO 3: multi-depends array ===")
    # n2 and n3 both depend on n1; n4 depends on [n2.done, n3.done]
    p = {
        "id": "p_multi_depends",
        "name": "Multi Depends",
        "loop_mode": "iterative",
        "nodes": [
            {
                "id": "n1",
                "type": "SetVars",
                "config": {
                    "variables": [
                        {"id": "v1", "name": "A", "mode": "string", "value": "foo"},
                        {"id": "v2", "name": "B", "mode": "string", "value": "bar"}
                    ]
                },
                "in": {}
            },
            {
                "id": "n2",
                "type": "Return",
                "config": {
                    "target_format": "openai",
                    "text_template": "[[A]]"
                },
                "in": {
                    "depends": "n1.done"
                }
            },
            {
                "id": "n3",
                "type": "Return",
                "config": {
                    "target_format": "openai",
                    "text_template": "[[B]]"
                },
                "in": {
                    "depends": "n1.done"
                }
            },
            {
                "id": "n4",
                "type": "Return",
                "config": {
                    "target_format": "openai",
                    "text_template": "[[OUT2]] + [[OUT3]]"
                },
                "in": {
                    "depends": ["n2.done", "n3.done"]
                }
            }
        ]
    }
    exe = PipelineExecutor(p)
    try:
        out = await exe.run(_base_ctx())
        print("OK:", _pp(out))
    except Exception as e:
        print("ERR:", type(e).__name__, str(e))

async def scenario_gate_only_dependency():
    print("\n=== SCENARIO 4: gate-only dependency (no real parents) ===")
    # nThen depends only on nIf.true (should run only when gate becomes true)
    p = {
        "id": "p_gate_only",
        "name": "Gate Only",
        "loop_mode": "iterative",
        "nodes": [
            {
                "id": "n1",
                "type": "SetVars",
                "config": {
                    "variables": [
                        {"id": "v1", "name": "FLAG", "mode": "string", "value": "yes"}
                    ]
                },
                "in": {}
            },
            {
                "id": "nIf",
                "type": "If",
                "config": {
                    "expr": "[[FLAG]] == 'yes'"
                },
                "in": {
                    "depends": "n1.done"
                }
            },
            {
                "id": "nThen",
                "type": "Return",
                "config": {
                    "target_format": "openai",
                    "text_template": "then-branch"
                },
                "in": {
                    # gate-only
                    "depends": "nIf.true"
                }
            }
        ]
    }
    exe = PipelineExecutor(p)
    try:
        out = await exe.run(_base_ctx())
        print("OK:", _pp(out))
    except Exception as e:
        print("ERR:", type(e).__name__, str(e))

async def scenario_provider_prompt_empty_json_error():
    print("\n=== SCENARIO 5: ProviderCall with empty PROMPT causing JSON error (collect logs) ===")
    # Template has [[PROMPT]] surrounded by commas; blocks are empty => PROMPT = ""
    # Resulting JSON likely invalid -> ExecutionError expected before any network call.
    p = {
        "id": "p_prompt_empty",
        "name": "Prompt Empty JSON Error",
        "loop_mode": "dag",
        "nodes": [
            {
                "id": "n1",
                "type": "ProviderCall",
                "config": {
                    "provider": "openai",
                    "provider_configs": {
                        "openai": {
                            "base_url": "https://api.openai.com",
                            "endpoint": "/v1/chat/completions",
                            "headers": "{\"Authorization\":\"Bearer TEST\"}",
                            "template": "{\n  \"model\": \"{{ model }}\",\n  [[PROMPT]],\n  \"temperature\": 0.1\n}"
                        }
                    },
                    "blocks": []  # empty -> PROMPT empty
                },
                "in": {}
            }
        ]
    }
    exe = PipelineExecutor(p)
    try:
        out = await exe.run(_base_ctx())
        print("UNEXPECTED_OK:", _pp(out))
    except Exception as e:
        print("EXPECTED_ERROR:", type(e).__name__, str(e))

async def scenario_rawforward_vendor_unknown():
    print("\n=== SCENARIO 6: RawForward vendor unknown (non-JSON body simulated) ===")
    # We simulate incoming.json as a plain string that doesn't look like any known vendor payload.
    # RawForward will try vendor detect, fail and raise ExecutionError (collect logs, do not fix).
    p = {
        "id": "p_rawforward_unknown",
        "name": "RawForward Unknown",
        "loop_mode": "dag",
        "nodes": [
            {
                "id": "n1",
                "type": "RawForward",
                "config": {
                    # No base_url -> autodetect vendor from incoming.json (will fail)
                    "passthrough_headers": True,
                    "extra_headers": "{}"
                },
                "in": {}
            }
        ]
    }
    exe = PipelineExecutor(p)
    ctx = _base_ctx()
    ctx["incoming"] = {
        "method": "POST",
        "url": "http://example.test/whatever",
        "path": "/whatever",
        "query": "",
        "headers": {"Content-Type": "text/plain"},
        "json": "raw-plain-body-simulated"  # NOT JSON object -> detect_vendor -> unknown
    }
    try:
        out = await exe.run(ctx)
        print("UNEXPECTED_OK:", _pp(out))
    except Exception as e:
        print("EXPECTED_ERROR:", type(e).__name__, str(e))

def run_all():
    async def main():
        await scenario_if_single_quotes_ok()
        await scenario_if_error_logging()
        await scenario_multi_depends_array()
        await scenario_gate_only_dependency()
        await scenario_provider_prompt_empty_json_error()
        await scenario_rawforward_vendor_unknown()
        print("\n=== EDGE CASES: DONE ===")
    asyncio.run(main())

if __name__ == "__main__":
    run_all()