"""Step definitions for agent_configurable_limits.feature.

These steps test the configurable ``max_dependencies`` parameter on
``ContextAnalysisAgent`` and the configurable ``max_context_files`` parameter
on ``PlanGenerationGraph``, covering:

- Default values preserve existing behaviour (10 / 5 respectively)
- Custom values are respected
- Non-positive values raise ``ValueError``
"""

from __future__ import annotations

from typing import Any

from behave import given, then, when
from langchain_community.llms import FakeListLLM

from cleveragents.agents.graphs.context_analysis import ContextAnalysisAgent
from cleveragents.agents.graphs.plan_generation import PlanGenerationGraph
from cleveragents.domain.models.core import Context

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

_DEFAULT_RESPONSES = [
    "Dependencies: ['os', 'sys', 'pathlib']",
    "Relevance: 0.8 - core module",
    "Summary: Generated context overview",
] * 30


def _make_llm() -> FakeListLLM:
    return FakeListLLM(responses=list(_DEFAULT_RESPONSES))


def _make_dep_string(count: int) -> str:
    """Return an LLM-style dependency string with *count* items."""
    items = ", ".join(f"dep_{i}" for i in range(count))
    return f"[{items}]"


def _make_contexts(count: int) -> list[Context]:
    """Return a list of *count* dummy Context objects."""
    return [
        Context(plan_id=1, path=f"src/file_{i}.py", content=f"content of file {i}")
        for i in range(count)
    ]


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


@given("the configurable limits module is importable")
def step_module_importable(context: Any) -> None:
    assert ContextAnalysisAgent is not None
    assert PlanGenerationGraph is not None


# ---------------------------------------------------------------------------
# ContextAnalysisAgent — default max_dependencies
# ---------------------------------------------------------------------------


@given("a ContextAnalysisAgent with default settings")
def step_create_agent_default(context: Any) -> None:
    context.agent = ContextAnalysisAgent(llm=_make_llm())


@given("a ContextAnalysisAgent with max_dependencies set to {limit:d}")
def step_create_agent_custom_max_deps(context: Any, limit: int) -> None:
    context.agent = ContextAnalysisAgent(llm=_make_llm(), max_dependencies=limit)


@when("I parse dependencies from a string with {count:d} items")
def step_parse_deps_with_count(context: Any, count: int) -> None:
    dep_string = _make_dep_string(count)
    context.parsed_deps = context.agent._parse_dependencies(dep_string)


@then("the result should contain exactly {expected:d} dependencies")
def step_check_dep_count(context: Any, expected: int) -> None:
    actual = len(context.parsed_deps)
    assert actual == expected, (
        f"Expected {expected} dependencies, got {actual}: {context.parsed_deps!r}"
    )


# ---------------------------------------------------------------------------
# ContextAnalysisAgent — ValueError for invalid max_dependencies
# ---------------------------------------------------------------------------


@when("I create a ContextAnalysisAgent with max_dependencies set to {limit:d}")
def step_create_agent_invalid_max_deps(context: Any, limit: int) -> None:
    context.raised_error = None
    try:
        ContextAnalysisAgent(llm=_make_llm(), max_dependencies=limit)
    except ValueError as exc:
        context.raised_error = exc


@then("a ValueError should be raised about max_dependencies")
def step_check_value_error_max_deps(context: Any) -> None:
    assert context.raised_error is not None, (
        "Expected ValueError but no error was raised"
    )
    assert "max_dependencies" in str(context.raised_error), (
        f"Expected 'max_dependencies' in error message, got: {context.raised_error!r}"
    )


# ---------------------------------------------------------------------------
# PlanGenerationGraph — default max_context_files
# ---------------------------------------------------------------------------


@given("a PlanGenerationGraph with default settings")
def step_create_graph_default(context: Any) -> None:
    context.graph = PlanGenerationGraph(llm=_make_llm())


@given("a PlanGenerationGraph with max_context_files set to {limit:d}")
def step_create_graph_custom_max_ctx(context: Any, limit: int) -> None:
    context.graph = PlanGenerationGraph(llm=_make_llm(), max_context_files=limit)


@when("I format a context summary with {count:d} context files")
def step_format_context_summary(context: Any, count: int) -> None:
    contexts = _make_contexts(count)
    context.summary = context.graph._format_context_summary(contexts)
    context.context_count = count


@then("the summary should contain exactly {expected:d} file entries")
def step_check_file_entries(context: Any, expected: int) -> None:
    # Each file entry starts with "File: "
    file_count = context.summary.count("File: ")
    assert file_count == expected, (
        f"Expected {expected} 'File:' entries in summary, got {file_count}.\n"
        f"Summary:\n{context.summary!r}"
    )


@then("the summary should mention {extra:d} more files")
def step_check_more_files_mention(context: Any, extra: int) -> None:
    assert f"... and {extra} more files" in context.summary, (
        f"Expected '... and {extra} more files' in summary.\n"
        f"Summary:\n{context.summary!r}"
    )


@then("the summary should not mention more files")
def step_check_no_more_files(context: Any) -> None:
    assert "more files" not in context.summary, (
        f"Expected no 'more files' mention in summary.\nSummary:\n{context.summary!r}"
    )


@then("the summary should be the no-context message")
def step_check_no_context_message(context: Any) -> None:
    assert context.summary == "No context files provided", (
        f"Expected 'No context files provided', got: {context.summary!r}"
    )


# ---------------------------------------------------------------------------
# PlanGenerationGraph — ValueError for invalid max_context_files
# ---------------------------------------------------------------------------


@when("I create a PlanGenerationGraph with max_context_files set to {limit:d}")
def step_create_graph_invalid_max_ctx(context: Any, limit: int) -> None:
    context.raised_error = None
    try:
        PlanGenerationGraph(llm=_make_llm(), max_context_files=limit)
    except ValueError as exc:
        context.raised_error = exc


@then("a ValueError should be raised about max_context_files")
def step_check_value_error_max_ctx(context: Any) -> None:
    assert context.raised_error is not None, (
        "Expected ValueError but no error was raised"
    )
    assert "max_context_files" in str(context.raised_error), (
        f"Expected 'max_context_files' in error message, got: {context.raised_error!r}"
    )
