"""Step definitions for lsp_tool_adapter_coverage.feature.

Covers uncovered lines in src/cleveragents/lsp/tool_adapter.py:
  - Line 198: ValueError when config is None
  - Lines 126-132: workspace-symbols handler (empty query / valid query)
  - Line 137: missing file_path guard
  - Line 141: diagnostics delegation
  - Lines 143-147: completions delegation
  - Lines 150-153: unimplemented capability fallback
"""

from __future__ import annotations

from unittest.mock import MagicMock

from behave import given, then, when

from cleveragents.lsp.errors import LspNotAvailableError
from cleveragents.lsp.models import LspCapability
from cleveragents.lsp.tool_adapter import (
    LspToolAdapter,
    _make_runtime_handler,
)

# ---------------------------------------------------------------------------
# Givens
# ---------------------------------------------------------------------------

_SERVER_NAME = "test/mock-server"


@given("ltacov an LspToolAdapter with no runtime")
def step_ltacov_adapter_no_runtime(context):
    context.ltacov_adapter = LspToolAdapter(runtime=None)


@given("ltacov a mock LspRuntime")
def step_ltacov_mock_runtime(context):
    context.ltacov_runtime = MagicMock()
    context.ltacov_runtime.get_diagnostics = MagicMock(return_value=[])
    context.ltacov_runtime.get_completions = MagicMock(return_value=[])


@given("ltacov the runtime get_diagnostics returns sample items")
def step_ltacov_diagnostics_returns_items(context):
    context.ltacov_sample_items = [{"message": "unused import", "severity": 2}]
    context.ltacov_runtime.get_diagnostics.return_value = context.ltacov_sample_items


@given("ltacov the runtime get_completions returns sample items")
def step_ltacov_completions_returns_items(context):
    context.ltacov_sample_items = [{"label": "my_func", "kind": 3}]
    context.ltacov_runtime.get_completions.return_value = context.ltacov_sample_items


@given("ltacov a runtime handler for workspace_symbols capability")
def step_ltacov_handler_workspace_symbols(context):
    context.ltacov_handler = _make_runtime_handler(
        context.ltacov_runtime, _SERVER_NAME, LspCapability.WORKSPACE_SYMBOLS
    )


@given("ltacov a runtime handler for diagnostics capability")
def step_ltacov_handler_diagnostics(context):
    context.ltacov_handler = _make_runtime_handler(
        context.ltacov_runtime, _SERVER_NAME, LspCapability.DIAGNOSTICS
    )


@given("ltacov a runtime handler for completions capability")
def step_ltacov_handler_completions(context):
    context.ltacov_handler = _make_runtime_handler(
        context.ltacov_runtime, _SERVER_NAME, LspCapability.COMPLETIONS
    )


@given("ltacov a runtime handler for hover capability")
def step_ltacov_handler_hover(context):
    context.ltacov_handler = _make_runtime_handler(
        context.ltacov_runtime, _SERVER_NAME, LspCapability.HOVER
    )


@given("ltacov a runtime handler for references capability")
def step_ltacov_handler_references(context):
    context.ltacov_handler = _make_runtime_handler(
        context.ltacov_runtime, _SERVER_NAME, LspCapability.REFERENCES
    )


# ---------------------------------------------------------------------------
# Whens
# ---------------------------------------------------------------------------


@when("ltacov generate_tool_specs is called with None config")
def step_ltacov_generate_with_none(context):
    context.ltacov_error = None
    try:
        context.ltacov_adapter.generate_tool_specs(None)
    except ValueError as exc:
        context.ltacov_error = exc


@when("ltacov the handler is called with no query argument")
def step_ltacov_handler_no_query(context):
    context.ltacov_error = None
    context.ltacov_result = None
    try:
        context.ltacov_result = context.ltacov_handler()
    except Exception as exc:
        context.ltacov_error = exc


@when('ltacov the handler is called with query "{query}"')
def step_ltacov_handler_with_query(context, query):
    context.ltacov_error = None
    context.ltacov_result = None
    try:
        context.ltacov_result = context.ltacov_handler(query=query)
    except LspNotAvailableError as exc:
        context.ltacov_error = exc


@when("ltacov the handler is called with no file_path argument")
def step_ltacov_handler_no_filepath(context):
    context.ltacov_error = None
    context.ltacov_result = None
    try:
        context.ltacov_result = context.ltacov_handler()
    except Exception as exc:
        context.ltacov_error = exc


@when('ltacov the handler is called with file_path "{file_path}"')
def step_ltacov_handler_with_filepath(context, file_path):
    context.ltacov_error = None
    context.ltacov_result = None
    try:
        context.ltacov_result = context.ltacov_handler(file_path=file_path)
    except LspNotAvailableError as exc:
        context.ltacov_error = exc


@when(
    'ltacov the handler is called with file_path "{file_path}" line {line:d} column {column:d}'
)
def step_ltacov_handler_with_filepath_line_col(context, file_path, line, column):
    context.ltacov_error = None
    context.ltacov_result = None
    try:
        context.ltacov_result = context.ltacov_handler(
            file_path=file_path, line=line, column=column
        )
    except LspNotAvailableError as exc:
        context.ltacov_error = exc


# ---------------------------------------------------------------------------
# Thens
# ---------------------------------------------------------------------------


@then('ltacov a ValueError with message "{msg}" is raised')
def step_ltacov_valueerror_raised(context, msg):
    assert context.ltacov_error is not None, "Expected a ValueError but none was raised"
    assert isinstance(context.ltacov_error, ValueError), (
        f"Expected ValueError, got {type(context.ltacov_error).__name__}"
    )
    assert msg in str(context.ltacov_error), (
        f"Expected message to contain '{msg}', got '{context.ltacov_error}'"
    )


@then('ltacov the result is an error dict saying "{expected_msg}"')
def step_ltacov_error_dict(context, expected_msg):
    assert context.ltacov_error is None, (
        f"Expected no exception, but got {context.ltacov_error}"
    )
    assert isinstance(context.ltacov_result, dict), (
        f"Expected dict, got {type(context.ltacov_result)}"
    )
    assert "error" in context.ltacov_result, (
        f"Expected 'error' key in result: {context.ltacov_result}"
    )
    assert expected_msg in context.ltacov_result["error"], (
        f"Expected error to contain '{expected_msg}', "
        f"got '{context.ltacov_result['error']}'"
    )


@then('ltacov an LspNotAvailableError is raised mentioning "{capability}"')
def step_ltacov_lsp_not_available(context, capability):
    assert context.ltacov_error is not None, (
        "Expected LspNotAvailableError but none was raised"
    )
    assert isinstance(context.ltacov_error, LspNotAvailableError), (
        f"Expected LspNotAvailableError, got {type(context.ltacov_error).__name__}"
    )
    assert capability in str(context.ltacov_error), (
        f"Expected error message to mention '{capability}', "
        f"got '{context.ltacov_error}'"
    )


@then('ltacov the result contains a "{key}" key with the sample items')
def step_ltacov_result_with_key(context, key):
    assert context.ltacov_error is None, (
        f"Expected no exception, but got {context.ltacov_error}"
    )
    assert isinstance(context.ltacov_result, dict), (
        f"Expected dict, got {type(context.ltacov_result)}"
    )
    assert key in context.ltacov_result, (
        f"Expected key '{key}' in result: {context.ltacov_result}"
    )
    assert context.ltacov_result[key] == context.ltacov_sample_items, (
        f"Expected {context.ltacov_sample_items}, got {context.ltacov_result[key]}"
    )
