"""Step definitions for definition-of-done gating tests."""

from __future__ import annotations

from behave import given, then, when
from behave.runner import Context

from cleveragents.domain.models.core.definition_of_done import (
    DoDCriterion,
    DoDResult,
    DoDStatus,
    DoDSummary,
    TextMatchEvaluator,
    parse_dod_criteria,
    render_dod_template,
)

__all__: list[str] = []


# ---------------------------------------------------------------------------
# Background
# ---------------------------------------------------------------------------


@given("a definition of done test environment")
def step_given_dod_env(context: Context) -> None:
    context.dod_criteria = None  # list[DoDCriterion] | None
    context.dod_rendered = None  # str | None
    context.dod_results = None  # list[DoDResult] | None
    context.dod_context = {}  # dict[str, Any]
    context.dod_summary = None  # DoDSummary | None


# ---------------------------------------------------------------------------
# Given steps
# ---------------------------------------------------------------------------


@given('DoD criteria from "{text}"')
def step_given_criteria_from(context: Context, text: str) -> None:
    resolved = text.replace("\\n", "\n")
    context.dod_criteria = parse_dod_criteria(resolved)


@given('evaluation context with key "{key}" value "{value}"')
def step_given_eval_context(context: Context, key: str, value: str) -> None:
    context.dod_context[key] = value


@given("an empty evaluation context")
def step_given_empty_context(context: Context) -> None:
    context.dod_context = {}


@given('a DoD criterion with text "{text}" and pattern "{pattern}"')
def step_given_criterion_with_pattern(
    context: Context, text: str, pattern: str
) -> None:
    context.dod_criteria = [DoDCriterion(index=0, text=text, pattern=pattern)]


@given("a DoDSummary with {p} passed and {f} failed")
def step_given_summary(context: Context, p: str, f: str) -> None:
    results: list[DoDResult] = []
    for i in range(int(p)):
        results.append(
            DoDResult(
                criterion=DoDCriterion(index=i, text=f"crit-{i}"),
                status=DoDStatus.PASSED,
                reasoning="ok",
            )
        )
    for i in range(int(f)):
        results.append(
            DoDResult(
                criterion=DoDCriterion(index=int(p) + i, text=f"fail-{i}"),
                status=DoDStatus.FAILED,
                reasoning="not ok",
            )
        )
    context.dod_summary = DoDSummary(
        plan_id="01DODTEST0000000000001",
        definition_of_done="test dod",
        results=results,
    )


@given("an empty DoDSummary")
def step_given_empty_summary(context: Context) -> None:
    context.dod_summary = DoDSummary(
        plan_id="01DODTEST0000000000001",
        definition_of_done="empty",
        results=[],
    )


@given("a DoDSummary with all criteria skipped")
def step_given_all_skipped_summary(context: Context) -> None:
    results = [
        DoDResult(
            criterion=DoDCriterion(index=i, text=f"skip-{i}"),
            status=DoDStatus.SKIPPED,
            reasoning="skipped",
        )
        for i in range(2)
    ]
    context.dod_summary = DoDSummary(
        plan_id="01DODTEST0000000000001",
        definition_of_done="skip test",
        results=results,
    )


@given("a DoD template that is {n} characters long")
def step_given_long_dod_template(context: Context, n: str) -> None:
    context.dod_oversized_template = "x" * int(n)


@given('a DoD criterion with text "{text}" and pattern that is {n} chars')
def step_given_long_pattern(context: Context, text: str, n: str) -> None:
    context.dod_criteria = [
        DoDCriterion(index=0, text=text, pattern="a" * int(n)),
    ]


@given("a short DoD template with a huge argument value")
def step_given_huge_value_template(context: Context) -> None:
    context.dod_short_template = "Result: {val}"
    context.dod_huge_value = "x" * 60_000


# ---------------------------------------------------------------------------
# When steps
# ---------------------------------------------------------------------------


@when('I parse the DoD text "{text}"')
def step_when_parse(context: Context, text: str) -> None:
    resolved = text.replace("\\n", "\n")
    context.dod_criteria = parse_dod_criteria(resolved)


@when("I parse an empty DoD text")
def step_when_parse_empty(context: Context) -> None:
    context.dod_criteria = parse_dod_criteria("")


@when('I render the DoD template "{template}" with argument "{key}" as "{value}"')
def step_when_render(context: Context, template: str, key: str, value: str) -> None:
    context.dod_rendered = render_dod_template(template, {key: value})


@when(
    'I render the DoD template "{template}" with arguments "{k1}"="{v1}" and "{k2}"="{v2}"'
)
def step_when_render_multi(
    context: Context, template: str, k1: str, v1: str, k2: str, v2: str
) -> None:
    context.dod_rendered = render_dod_template(template, {k1: v1, k2: v2})


@when('I render the oversized DoD template with argument "{key}" as "{value}"')
def step_when_render_oversized(context: Context, key: str, value: str) -> None:
    try:
        context.dod_rendered = render_dod_template(
            context.dod_oversized_template, {key: value}
        )
        context.dod_render_error = None
    except ValueError as exc:
        context.dod_render_error = exc


@when("I render the DoD template that produces oversized output")
def step_when_render_oversized_output(context: Context) -> None:
    try:
        context.dod_rendered = render_dod_template(
            context.dod_short_template, {"val": context.dod_huge_value}
        )
        context.dod_render_error = None
    except ValueError as exc:
        context.dod_render_error = exc


@when("I evaluate the criteria with TextMatchEvaluator")
def step_when_evaluate(context: Context) -> None:
    evaluator = TextMatchEvaluator()
    context.dod_results = evaluator.evaluate(context.dod_criteria, context.dod_context)


# ---------------------------------------------------------------------------
# Then steps
# ---------------------------------------------------------------------------


@then("there should be {n} criteria parsed")
def step_then_criteria_count(context: Context, n: str) -> None:
    assert context.dod_criteria is not None
    assert len(context.dod_criteria) == int(n), (
        f"Expected {n} criteria, got {len(context.dod_criteria)}"
    )


@then('criterion {i} text should be "{text}"')
def step_then_criterion_text(context: Context, i: str, text: str) -> None:
    assert context.dod_criteria is not None
    idx = int(i)
    assert idx < len(context.dod_criteria)
    assert context.dod_criteria[idx].text == text, (
        f"Expected text '{text}', got '{context.dod_criteria[idx].text}'"
    )


@then('the rendered DoD should be "{expected}"')
def step_then_rendered(context: Context, expected: str) -> None:
    assert context.dod_rendered == expected, (
        f"Expected '{expected}', got '{context.dod_rendered}'"
    )


@then('the rendered DoD should contain "{text}"')
def step_then_rendered_contains(context: Context, text: str) -> None:
    assert context.dod_rendered is not None
    assert text in context.dod_rendered


@then("all criteria should have passed")
def step_then_all_passed(context: Context) -> None:
    assert context.dod_results is not None
    for r in context.dod_results:
        assert r.status == DoDStatus.PASSED, (
            f"Criterion '{r.criterion.text}' has status '{r.status.value}'"
        )


@then('criterion {i} should have status "{status}"')
def step_then_criterion_status(context: Context, i: str, status: str) -> None:
    assert context.dod_results is not None
    idx = int(i)
    assert idx < len(context.dod_results)
    assert context.dod_results[idx].status == DoDStatus(status), (
        f"Expected status '{status}', got '{context.dod_results[idx].status.value}'"
    )


@then('criterion {i} reasoning should contain "{text}"')
def step_then_criterion_reasoning(context: Context, i: str, text: str) -> None:
    assert context.dod_results is not None
    idx = int(i)
    assert text in context.dod_results[idx].reasoning, (
        f"Expected reasoning to contain '{text}', "
        f"got: {context.dod_results[idx].reasoning}"
    )


@then("the summary should report all passed")
def step_then_summary_all_passed(context: Context) -> None:
    assert context.dod_summary is not None
    assert context.dod_summary.all_passed


@then("the summary should not report all passed")
def step_then_summary_not_all_passed(context: Context) -> None:
    assert context.dod_summary is not None
    assert not context.dod_summary.all_passed


@then("the DoD summary total should be {n}")
def step_then_dod_summary_total(context: Context, n: str) -> None:
    assert context.dod_summary is not None
    assert context.dod_summary.total == int(n)


@then("the summary failed count should be {n}")
def step_then_summary_failed(context: Context, n: str) -> None:
    assert context.dod_summary is not None
    assert context.dod_summary.failed == int(n)


@then("the summary should be empty")
def step_then_summary_empty(context: Context) -> None:
    assert context.dod_summary is not None
    assert context.dod_summary.is_empty


@then('the summary CLI dict should have key "{key}"')
def step_then_cli_dict_key(context: Context, key: str) -> None:
    assert context.dod_summary is not None
    cli_dict = context.dod_summary.to_cli_dict()
    assert key in cli_dict


@then("the validation summary should have required_passed {n}")
def step_then_val_summary_passed(context: Context, n: str) -> None:
    assert context.dod_summary is not None
    vs = context.dod_summary.to_validation_summary()
    assert vs["required_passed"] == int(n)


@then("the validation summary should have required_failed {n}")
def step_then_val_summary_failed(context: Context, n: str) -> None:
    assert context.dod_summary is not None
    vs = context.dod_summary.to_validation_summary()
    assert vs["required_failed"] == int(n)


@then("the validation summary should have dod_evaluated true")
def step_then_val_summary_dod(context: Context) -> None:
    assert context.dod_summary is not None
    vs = context.dod_summary.to_validation_summary()
    assert vs["dod_evaluated"] is True


@then("the summary skipped count should be {n}")
def step_then_summary_skipped(context: Context, n: str) -> None:
    assert context.dod_summary is not None
    assert context.dod_summary.skipped == int(n), (
        f"Expected skipped={n}, got {context.dod_summary.skipped}"
    )


@then('the DoD render should fail with "{text}"')
def step_then_dod_render_error(context: Context, text: str) -> None:
    assert context.dod_render_error is not None, "Expected a ValueError"
    assert text in str(context.dod_render_error), (
        f"Expected '{text}' in error: {context.dod_render_error}"
    )
