"""Workflow-defined run argument parsing and binding."""

from __future__ import annotations

import pytest

from attractor.workflow import (
    ValidationFailed,
    WorkflowArgumentsError,
    bind_workflow_arguments,
    parse,
    parse_argument_definitions,
    validate,
)


def _graph(arguments: str) -> str:
    return f"""\
digraph Args {{
    graph [
        arguments = "{arguments}"
    ]
    start [shape=Mdiamond]
    exit  [shape=Msquare]
    start -> exit
}}
"""


# [unit->REQ-EXEC-RUN-ARGUMENTS]
def test_parse_argument_definitions() -> None:
    graph = parse(
        _graph(
            """
            issue_url { type: url; required: true; help: GitHub issue URL; }
            note { type: string; required: false; help: Optional note; }
            """
        )
    )

    definitions = parse_argument_definitions(validate(graph))

    assert [arg.name for arg in definitions] == ["issue_url", "note"]
    assert definitions[0].type == "url"
    assert definitions[0].required is True
    assert definitions[1].type == "string"
    assert definitions[1].required is False


# [unit->REQ-EXEC-RUN-ARGUMENTS]
def test_bind_workflow_arguments_validates_url() -> None:
    definitions = parse_argument_definitions(
        validate(parse(_graph("issue_url { type: url; }")))
    )

    bound = bind_workflow_arguments(
        definitions, ("https://github.com/owner/repo/issues/123",)
    )

    assert bound == {
        "issue_url": "https://github.com/owner/repo/issues/123"
    }


# [unit->REQ-EXEC-RUN-ARGUMENTS]
def test_bind_rejects_missing_required() -> None:
    definitions = parse_argument_definitions(
        validate(parse(_graph("issue_url { type: url; }")))
    )

    with pytest.raises(WorkflowArgumentsError, match="missing required"):
        bind_workflow_arguments(definitions, ())


# [unit->REQ-EXEC-RUN-ARGUMENTS]
def test_bind_rejects_invalid_url() -> None:
    definitions = parse_argument_definitions(
        validate(parse(_graph("issue_url { type: url; }")))
    )

    with pytest.raises(WorkflowArgumentsError, match="absolute http"):
        bind_workflow_arguments(definitions, ("not-a-url",))


# [unit->REQ-EXEC-RUN-ARGUMENTS]
def test_validate_rejects_bad_argument_declaration() -> None:
    graph = parse(_graph("IssueUrl { type: url; }"))

    with pytest.raises(ValidationFailed, match="invalid graph arguments"):
        validate(graph)
