"""Step definitions for validation CLI uncovered branches.

Covers two gaps in ``cleveragents.cli.commands.validation``:

1. ``_get_tool_registry_service()`` (lines 75-95) — the full construction path
   that imports from the container, creates an engine/session-factory, builds
   repositories, and returns a ``ToolRegistryService``.

2. ``detach`` command branch L356→360 — when the user confirms the detach
   prompt but ``detach_validation`` returns ``False`` (attachment not found).
"""

from __future__ import annotations

from typing import Any
from unittest.mock import MagicMock, patch

from behave import given, then, when
from behave.runner import Context
from typer.testing import CliRunner

from cleveragents.cli.commands.validation import app as validation_app

_runner = CliRunner()

# ---------------------------------------------------------------------------
# Patch targets
# ---------------------------------------------------------------------------

# We patch get_container *inside* the validation module's lazy import so the
# real function body executes but uses our mock container.
_PATCH_GET_CONTAINER = "cleveragents.application.container.get_container"

# For the detach scenario we still patch the whole helper to isolate DB access.
_PATCH_VAL_SVC = "cleveragents.cli.commands.validation._get_tool_registry_service"


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


@given("a validation cli branch test runner with mocks")
def step_validation_cli_branch_background(context: Context) -> None:
    context.vcb_runner = _runner
    context.vcb_result = None
    context.vcb_returned_service: Any = None
    context.vcb_mock_service: MagicMock | None = None


# ---------------------------------------------------------------------------
# Scenario 1: _get_tool_registry_service construction path (L75-95)
# ---------------------------------------------------------------------------


@given("the validation cli branch DI container provides a database url")
def step_vcb_di_container(context: Context) -> None:
    """Set up a mock container whose ``tool_registry_service()`` returns a
    real ToolRegistryService backed by an in-memory SQLite database.

    After Forgejo #3006, ``_get_tool_registry_service`` delegates directly to
    ``container.tool_registry_service()`` — no manual engine/sessionmaker
    construction occurs in the function itself.
    """
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker

    from cleveragents.application.services.tool_registry_service import (
        ToolRegistryService,
    )
    from cleveragents.infrastructure.database.repositories import (
        ToolRegistryRepository,
        ValidationAttachmentRepository,
    )

    engine = create_engine("sqlite:///:memory:", echo=False)
    factory = sessionmaker(bind=engine, expire_on_commit=False)
    tool_repo = ToolRegistryRepository(session_factory=factory)
    attachment_repo = ValidationAttachmentRepository(session_factory=factory)
    real_service = ToolRegistryService(
        tool_repo=tool_repo,
        attachment_repo=attachment_repo,
    )

    mock_container = MagicMock()
    mock_container.tool_registry_service.return_value = real_service
    context.vcb_mock_container = mock_container


@when("the validation cli branch _get_tool_registry_service is called")
def step_vcb_call_get_tool_registry_service(context: Context) -> None:
    from cleveragents.cli.commands.validation import _get_tool_registry_service

    with patch(_PATCH_GET_CONTAINER, return_value=context.vcb_mock_container):
        context.vcb_returned_service = _get_tool_registry_service()


@then("the validation cli branch returned service should be a ToolRegistryService")
def step_vcb_verify_service_type(context: Context) -> None:
    from cleveragents.application.services.tool_registry_service import (
        ToolRegistryService,
    )

    assert context.vcb_returned_service is not None, (
        "_get_tool_registry_service returned None"
    )
    assert isinstance(context.vcb_returned_service, ToolRegistryService), (
        f"Expected ToolRegistryService, got {type(context.vcb_returned_service)}"
    )


# ---------------------------------------------------------------------------
# Scenario 2: detach — user confirms but attachment not found (L356→360)
# ---------------------------------------------------------------------------


@given("the validation cli branch detach service returns false")
def step_vcb_detach_service_false(context: Context) -> None:
    """Prepare a mock service whose ``detach_validation`` returns ``False``."""
    svc = MagicMock()
    svc.detach_validation.return_value = False
    context.vcb_mock_service = svc


@when('the validation cli branch detach is invoked with confirmation for "{att_id}"')
def step_vcb_detach_with_confirmation(context: Context, att_id: str) -> None:
    """Invoke the detach command *without* ``--yes`` and feed ``y`` to the
    confirmation prompt so the code reaches ``service.detach_validation``
    which returns ``False`` — covering L356→360."""
    with patch(_PATCH_VAL_SVC, return_value=context.vcb_mock_service):
        context.vcb_result = _runner.invoke(
            validation_app,
            ["detach", att_id],
            input="y\n",
        )


@then("the validation cli branch detach result should abort with not found")
def step_vcb_detach_abort_not_found(context: Context) -> None:
    result = context.vcb_result
    assert result is not None, "No CLI result captured"
    # The command should abort (non-zero exit)
    assert result.exit_code != 0, (
        f"Expected non-zero exit, got {result.exit_code}. Output: {result.output}"
    )
    assert "Attachment not found" in result.output, (
        f"Expected 'Attachment not found' in output, got: {result.output}"
    )
