"""Step definitions for container_id_integration.feature."""

from __future__ import annotations

import contextlib
from io import StringIO
from typing import Any
from unittest.mock import MagicMock, patch

import click
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.resource import Resource
from cleveragents.infrastructure.database.models import Base
from cleveragents.resource.handlers.devcontainer import DevcontainerHandler

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


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


def _patch_cid_service(context: Context) -> Any:
    """Monkey-patch the CLI 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_cid_service(context)

    resource_mod._get_registry_service = _mock_get  # type: ignore[assignment]
    return orig_fn


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

    resource_mod._get_registry_service = orig_fn  # type: ignore[assignment]


def _capture_cid_output(func: Any, *args: Any, **kwargs: Any) -> tuple[str, bool]:
    """Run a CLI function capturing its console output and success status."""
    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  # type: ignore[assignment]

    failed = False
    try:
        with contextlib.redirect_stdout(buf):
            func(*args, **kwargs)
    except (SystemExit, click.exceptions.Abort):
        failed = True
    except Exception:
        failed = True
    finally:
        resource_mod.console = orig_console  # type: ignore[assignment]

    return buf.getvalue(), failed


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


@given("a fresh in-memory resource registry for container_id")
def step_cid_fresh_registry(context: Context) -> None:
    """Set up a fresh in-memory database for container_id tests."""
    engine = create_engine("sqlite:///:memory:", echo=False)
    Base.metadata.create_all(engine)
    factory = sessionmaker(bind=engine, expire_on_commit=False)
    context.cid_service = ResourceRegistryService(session_factory=factory)
    context.cid_output = ""
    context.cid_failed = False


@given("built-in types are bootstrapped for container_id")
def step_cid_bootstrap_builtins(context: Context) -> None:
    """Bootstrap built-in resource types for container_id tests."""
    service = _make_cid_service(context)
    service.bootstrap_builtin_types()


@given('a container-instance resource with stored container_id "{cid}"')
def step_cid_resource_with_stored_id(context: Context, cid: str) -> None:
    """Create a Resource domain object with container_id in properties."""
    resource = MagicMock(spec=Resource)
    resource.properties = {"container_id": cid}
    resource.location = None
    context.cid_mock_resource = resource
    context.cid_docker_called = False


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


@when('I run resource add container-instance with container-id "{cid}"')
def step_cid_run_add_with_container_id(context: Context, cid: str) -> None:
    """Run resource_add for container-instance with --container-id."""
    from cleveragents.cli.commands.resource import resource_add

    orig = _patch_cid_service(context)
    try:
        output, failed = _capture_cid_output(
            resource_add,
            type_name="container-instance",
            name="local/test-ctr",
            path=None,
            branch=None,
            description=None,
            image=None,
            container_id=cid,
            mount=None,
            clone_into=None,
            update=False,
            read_only=False,
            fmt="rich",
        )
        context.cid_output = output
        context.cid_failed = failed
    finally:
        _unpatch_cid_service(orig)


@when("I run resource add container-instance with both container-id and image")
def step_cid_run_add_both_flags(context: Context) -> None:
    """Run resource_add with both --container-id and --image (should fail)."""
    from cleveragents.cli.commands.resource import resource_add

    orig = _patch_cid_service(context)
    try:
        output, failed = _capture_cid_output(
            resource_add,
            type_name="container-instance",
            name="local/test-ctr",
            path=None,
            branch=None,
            description=None,
            image="ubuntu:latest",
            container_id="abc123def456",
            mount=None,
            clone_into=None,
            update=False,
            read_only=False,
            fmt="rich",
        )
        context.cid_output = output
        context.cid_failed = failed
    finally:
        _unpatch_cid_service(orig)


@when("I run resource add container-instance with no image or container-id")
def step_cid_run_add_no_flags(context: Context) -> None:
    """Run resource_add for container-instance with neither flag (should fail)."""
    from cleveragents.cli.commands.resource import resource_add

    orig = _patch_cid_service(context)
    try:
        output, failed = _capture_cid_output(
            resource_add,
            type_name="container-instance",
            name="local/test-ctr",
            path=None,
            branch=None,
            description=None,
            image=None,
            container_id=None,
            mount=None,
            clone_into=None,
            update=False,
            read_only=False,
            fmt="rich",
        )
        context.cid_output = output
        context.cid_failed = failed
    finally:
        _unpatch_cid_service(orig)


@when('I show the container_id resource "{name}"')
def step_cid_show_resource(context: Context, name: str) -> None:
    """Run resource_show for the given resource name."""
    from cleveragents.cli.commands.resource import resource_show

    orig = _patch_cid_service(context)
    try:
        output, failed = _capture_cid_output(
            resource_show,
            resource=name,
            fmt="rich",
        )
        context.cid_show_output = output
        context.cid_show_failed = failed
    finally:
        _unpatch_cid_service(orig)


@when("I call _find_running_container on the resource")
def step_cid_call_find_running_container(context: Context) -> None:
    """Call DevcontainerHandler._find_running_container with a mock subprocess."""
    with patch("subprocess.run") as mock_run:
        context.cid_result = DevcontainerHandler._find_running_container(
            context.cid_mock_resource
        )
        context.cid_docker_called = mock_run.called


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


@then("the container_id command should succeed")
def step_cid_command_succeeded(context: Context) -> None:
    """Assert the last container_id CLI command succeeded."""
    assert not context.cid_failed, (
        f"Expected command to succeed but it failed. Output:\n{context.cid_output}"
    )


@then("the container_id command should fail")
def step_cid_command_failed(context: Context) -> None:
    """Assert the last container_id CLI command failed."""
    assert context.cid_failed, (
        f"Expected command to fail but it succeeded. Output:\n{context.cid_output}"
    )


@then('the container_id output should contain "{text}"')
def step_cid_output_contains(context: Context, text: str) -> None:
    """Assert the container_id CLI output contains the expected text."""
    output = context.cid_output
    assert text.lower() in output.lower(), (
        f"Expected '{text}' in output, got:\n{output}"
    )


@then('the container_id show output should contain "{text}"')
def step_cid_show_output_contains(context: Context, text: str) -> None:
    """Assert the container_id show output contains the expected text."""
    output = context.cid_show_output
    assert text in output, f"Expected '{text}' in show output, got:\n{output}"


@then('the container_id find result should be "{expected}"')
def step_cid_result_equals(context: Context, expected: str) -> None:
    """Assert _find_running_container returned the expected container ID."""
    assert context.cid_result == expected, (
        f"Expected result '{expected}', got '{context.cid_result}'"
    )


@then("no docker subprocess should have been called")
def step_cid_no_docker_called(context: Context) -> None:
    """Assert that subprocess.run was NOT called (docker was not invoked)."""
    assert not context.cid_docker_called, (
        "Expected no docker subprocess call, but subprocess.run was invoked"
    )
