"""Step definitions added for review fixes S46, S48, S50.

S46: Parameterized sanitization Scenario Outline steps.
S50: Negative / edge-case scenarios for retry_policy_wiring.feature.
"""

from __future__ import annotations

import threading
from typing import Any

from behave import given, then, when
from pydantic import ValidationError

# ---------------------------------------------------------------------------
# S46: Parameterized sanitization Scenario Outline
# ---------------------------------------------------------------------------

_SANITIZE_INPUTS: dict[str, str] = {
    "database_url": "Connection failed: postgres://admin:s3cret@db.host/mydb",
    "api_key": "Auth failed: api_key=sk-12345abcdef token=xyz789",
    "auth_header": "Request failed: Authorization: Bearer sk-live-abc123xyz",
    "private_key": "Config error: private_key=AAAA-BBBB-CCCC-DDDD",
    "connection_string": "DB error: connection_string=Server=db;Password=s3cret",
    "access_key": "AWS error: access_key=AKIAIOSFODNN7EXAMPLE",
}


@when('I sanitize an error containing {sensitive_type} with secret "{secret}"')
def step_sanitize_parameterized(context: Any, sensitive_type: str, secret: str) -> None:
    from cleveragents.core.retry_patterns import _sanitize_error_message

    raw_msg = _SANITIZE_INPUTS[sensitive_type]
    error = RuntimeError(raw_msg)
    context.sanitized_outline = _sanitize_error_message(error)
    context.expected_secret = secret


@then('the sanitized output should not contain "{secret}"')
def step_check_sanitized_no_secret(context: Any, secret: str) -> None:
    assert secret not in context.sanitized_outline, (
        f"Secret '{secret}' found in sanitized output: {context.sanitized_outline}"
    )


@then('the sanitized output should contain "{redacted_marker}"')
def step_check_sanitized_has_marker(context: Any, redacted_marker: str) -> None:
    assert redacted_marker in context.sanitized_outline, (
        f"Redacted marker '{redacted_marker}' not found in: {context.sanitized_outline}"
    )


# ---------------------------------------------------------------------------
# S50: Negative base_delay validation
# ---------------------------------------------------------------------------


@when("I attempt to create a RetryPolicyConfig with base_delay {value:g}")
def step_create_invalid_base_delay(context: Any, value: float) -> None:
    from cleveragents.domain.models.core.retry_policy import RetryPolicyConfig

    context.neg_validation_error = None
    try:
        RetryPolicyConfig(base_delay=value)
    except ValidationError as exc:
        context.neg_validation_error = exc


# ---------------------------------------------------------------------------
# S50: Disabled circuit breaker (enabled=false)
# ---------------------------------------------------------------------------


@given("I have a ServiceRetryWiring instance with disabled circuit breaker")
def step_create_wiring_disabled_cb(context: Any) -> None:
    from cleveragents.application.services.service_retry_wiring import (
        ServiceRetryWiring,
    )
    from cleveragents.config.settings import Settings

    settings = Settings(
        database_url="sqlite:///test_retry.db",
        mock_providers=True,
        env="test",
    )
    context.wiring = ServiceRetryWiring(settings)
    # Disable the CB for plan_service by removing it
    context.disabled_cb_service = "plan_service"
    cb = context.wiring.get_circuit_breaker(context.disabled_cb_service)
    if cb is not None:
        # Simulate disabled CB by removing it from the map
        context.wiring._circuit_breakers.pop(context.disabled_cb_service, None)


@when("I execute a function through the wiring for the disabled-CB service")
def step_execute_disabled_cb(context: Any) -> None:
    try:
        context.result = context.wiring.execute(
            service_name=context.disabled_cb_service,
            operation_name="disabled_cb_test",
            func=lambda: "disabled_cb_ok",
            idempotent=True,
        )
        context.exec_error = None
    except Exception as exc:
        context.exec_error = exc
        context.result = None


@then("the function should execute successfully without circuit breaker protection")
def step_check_disabled_cb_success(context: Any) -> None:
    assert context.exec_error is None, f"Unexpected error: {context.exec_error}"
    assert context.result == "disabled_cb_ok"


# ---------------------------------------------------------------------------
# S50: Retry exhaustion with low CB threshold
# ---------------------------------------------------------------------------


@given("I have a ServiceRetryWiring instance with low CB threshold")
def step_create_wiring_low_cb_threshold(context: Any) -> None:
    from cleveragents.application.services.service_retry_wiring import (
        ServiceRetryWiring,
    )
    from cleveragents.config.settings import Settings

    settings = Settings(
        database_url="sqlite:///test_retry.db",
        mock_providers=True,
        env="test",
    )
    context.wiring = ServiceRetryWiring(settings)
    # Lower the CB threshold to be <= max_attempts so exhaustion opens the CB
    cb = context.wiring.get_circuit_breaker("plan_service")
    if cb is not None:
        cb.failure_threshold = 2  # With max_attempts=3, 3 failures will open it


# ---------------------------------------------------------------------------
# S50: Concurrent failures open the circuit breaker
# ---------------------------------------------------------------------------


@given("I have a standalone CircuitBreaker with threshold {threshold:d}")
def step_create_standalone_cb_with_threshold(context: Any, threshold: int) -> None:
    from cleveragents.core.retry_patterns import CircuitBreaker

    context.standalone_cb = CircuitBreaker(
        failure_threshold=threshold,
        expected_exception=Exception,
    )


@when("{count:d} concurrent threads trigger failures on the circuit breaker")
def step_concurrent_cb_failures(context: Any, count: int) -> None:
    barrier = threading.Barrier(count, timeout=5)

    import contextlib

    def trigger_failure() -> None:
        barrier.wait()
        with contextlib.suppress(Exception):
            context.standalone_cb.call(_raise_runtime)

    threads = [threading.Thread(target=trigger_failure) for _ in range(count)]
    for t in threads:
        t.start()
    for t in threads:
        t.join(timeout=5)


def _raise_runtime() -> str:
    raise RuntimeError("concurrent failure")


# ---------------------------------------------------------------------------
# S50: Retry exhaustion opens circuit breaker
# ---------------------------------------------------------------------------


@then('the circuit breaker for "{name}" should be open after exhaustion')
def step_check_cb_open_after_exhaustion(context: Any, name: str) -> None:
    cb = context.wiring.get_circuit_breaker(name)
    assert cb is not None, f"No circuit breaker for {name}"
    # After max_attempts failures, the CB should have opened
    assert cb.state == "open", (
        f"Expected CB state 'open' after exhaustion, got '{cb.state}'"
    )
