"""Step definitions for resource list lifecycle state feature tests.

Tests that ``agents resource list`` shows the devcontainer lifecycle state
column as required by spec line 10726 (issue #2596).
"""

from __future__ import annotations

import json
from io import StringIO
from typing import Any
from unittest.mock import patch

from behave import given, then, when  # type: ignore[import-untyped]
from behave.runner import Context  # type: ignore[import-untyped]
from rich.console import Console
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from cleveragents.application.services.resource_registry_service import (
    ResourceRegistryService,
)
from cleveragents.domain.models.core.container_lifecycle import (
    ContainerLifecycleState,
    ContainerLifecycleTracker,
)
from cleveragents.infrastructure.database.models import Base


def _make_service(context: Context) -> ResourceRegistryService:
    """Create or return a cached ResourceRegistryService."""
    if not hasattr(context, "resource_cli_service"):
        engine = create_engine("sqlite:///:memory:", echo=False)
        Base.metadata.create_all(engine)
        factory = sessionmaker(bind=engine, expire_on_commit=False)
        context.resource_cli_service = ResourceRegistryService(session_factory=factory)
    return context.resource_cli_service


def _capture_output(func: Any, *args: Any, **kwargs: Any) -> tuple[str, bool]:
    """Run a CLI function capturing its console output and success status."""
    import contextlib

    buf = StringIO()
    console = Console(
        file=buf, width=200, no_color=True, highlight=False, force_terminal=False
    )

    import cleveragents.cli.commands.resource as resource_mod

    orig_console = resource_mod.console
    resource_mod.console = console

    failed = False
    try:
        with contextlib.redirect_stdout(buf):
            func(*args, **kwargs)
    except SystemExit:
        failed = True
    except Exception:
        failed = True
    finally:
        resource_mod.console = orig_console

    return buf.getvalue(), failed


def _patch_service(context: Context) -> Any:
    """Monkey-patch the container to return our in-memory service."""
    import cleveragents.cli.commands.resource as resource_mod

    orig_fn = resource_mod._get_registry_service

    def _mock_get() -> ResourceRegistryService:
        return _make_service(context)

    resource_mod._get_registry_service = _mock_get
    return orig_fn


def _unpatch_service(orig_fn: Any) -> None:
    """Restore original service getter."""
    import cleveragents.cli.commands.resource as resource_mod

    resource_mod._get_registry_service = orig_fn


# ---- Given steps ----


@given("a devcontainer-instance resource is registered")
def step_register_devcontainer_resource(context: Context) -> None:
    """Register a devcontainer-instance resource in the registry."""
    service = _make_service(context)
    service.bootstrap_builtin_types()
    resource = service.register_resource(
        type_name="devcontainer-instance",
        name=None,  # auto-discovered child resources have no name
        location="/tmp/test-project",
        description="Test devcontainer",
        read_only=False,
        properties=None,
    )
    context.devcontainer_resource_id = resource.resource_id
    # Default state is DETECTED — no tracker manipulation needed


@given("a container-instance resource is registered")
def step_register_container_resource(context: Context) -> None:
    """Register a container-instance resource in the registry."""
    service = _make_service(context)
    service.bootstrap_builtin_types()
    resource = service.register_resource(
        type_name="container-instance",
        name=None,  # auto-discovered child resources have no name
        location="/tmp/test-container",
        description="Test container",
        read_only=False,
        properties=None,
    )
    context.devcontainer_resource_id = resource.resource_id
    # Default state is DETECTED — no tracker manipulation needed


@given('the devcontainer lifecycle state is "{state}"')
def step_set_devcontainer_lifecycle_state(context: Context, state: str) -> None:
    """Set the lifecycle state of the registered devcontainer resource."""
    from cleveragents.resource.handlers._devcontainer_internals import (
        set_lifecycle_tracker,
    )

    resource_id = context.devcontainer_resource_id
    lifecycle_state = ContainerLifecycleState(state)
    tracker = ContainerLifecycleTracker(
        resource_id=resource_id,
        current_state=lifecycle_state,
    )
    set_lifecycle_tracker(tracker)


@given("the lifecycle tracker raises an error for the resource")
def step_lifecycle_tracker_raises_error(context: Context) -> None:
    """Configure the lifecycle tracker to raise a ValueError for the resource."""
    context.lifecycle_tracker_error_patch = patch(
        "cleveragents.cli.commands.resource.get_lifecycle_tracker",
        side_effect=ValueError("Simulated tracker failure"),
    )
    context.lifecycle_tracker_error_patch.start()


# ---- When steps ----


@when("I run resource list with all flag")
def step_run_resource_list_all(context: Context) -> None:
    """Run resource list --all command."""
    from cleveragents.cli.commands.resource import resource_list

    orig = _patch_service(context)
    try:
        output, failed = _capture_output(
            resource_list, type_filter=None, show_all=True, fmt="rich"
        )
        context.resource_cli_output = output
        context.resource_cli_failed = failed
    finally:
        _unpatch_service(orig)
        # Stop any active error patch
        if hasattr(context, "lifecycle_tracker_error_patch"):
            context.lifecycle_tracker_error_patch.stop()
            del context.lifecycle_tracker_error_patch


@when('I run resource list with all flag and format "{fmt}"')
def step_run_resource_list_all_fmt(context: Context, fmt: str) -> None:
    """Run resource list --all with specific format."""
    from cleveragents.cli.commands.resource import resource_list

    orig = _patch_service(context)
    try:
        output, failed = _capture_output(
            resource_list, type_filter=None, show_all=True, fmt=fmt
        )
        context.resource_cli_output = output
        context.resource_cli_failed = failed
    finally:
        _unpatch_service(orig)


# ---- Then steps ----


@then('the resource output should not contain "{text}"')
def step_output_should_not_contain(context: Context, text: str) -> None:
    """Assert the output does not contain the given text."""
    output = context.resource_cli_output
    assert text not in output, (
        f"Expected output NOT to contain {text!r}, but it did.\nOutput:\n{output}"
    )


@then("the resource JSON lifecycle_state should be null for non-container resources")
def step_json_lifecycle_state_null(context: Context) -> None:
    """Assert all resources in JSON output have null lifecycle_state."""
    output = context.resource_cli_output
    data = json.loads(output)
    if isinstance(data, list):
        for item in data:
            assert item.get("lifecycle_state") is None, (
                f"Expected lifecycle_state to be null for non-container resource, "
                f"got {item.get('lifecycle_state')!r}"
            )
    else:
        assert data.get("lifecycle_state") is None, (
            f"Expected lifecycle_state to be null, got {data.get('lifecycle_state')!r}"
        )


@then("the resource list command should succeed")
def step_resource_list_command_should_succeed(context: Context) -> None:
    """Assert the resource list command did not fail."""
    assert not context.resource_cli_failed, (
        f"Expected resource list command to succeed, but it failed.\n"
        f"Output:\n{context.resource_cli_output}"
    )
