"""Step definitions for validation_pipeline_stream_coverage.feature.

Exercises the _ThreadLocalStream helper methods that lack coverage:
- encoding property (line 58)
- writable() method (line 61)
- readable() method (line 64)
- flush() with active buffer (line 75)
- isatty() method (line 79)

All tests use a simple mock original stream — no real I/O needed.
"""

from __future__ import annotations

from typing import Any

from behave import given, then, when
from behave.runner import Context

from cleveragents.application.services.validation_pipeline import (
    _ThreadLocalStream,
)

# ---------------------------------------------------------------------------
# Mock original stream
# ---------------------------------------------------------------------------


class _MockOriginalStream:
    """Configurable mock that mimics a real stdout/stderr."""

    def __init__(self) -> None:
        self.encoding: str = "utf-8"
        self._has_encoding: bool = True
        self._isatty_return: bool = False
        self._written: list[str] = []
        self._flushed: bool = False

    def write(self, s: str) -> int:
        self._written.append(s)
        return len(s)

    def flush(self) -> None:
        self._flushed = True

    def isatty(self) -> bool:
        return self._isatty_return


class _NoEncodingStream:
    """Mock stream without an encoding attribute."""

    def write(self, s: str) -> int:
        return len(s)

    def flush(self) -> None:
        pass

    def isatty(self) -> bool:
        return False


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


@given("a thread-local stream wrapping a mock original stream")
def step_given_tl_stream(context: Context) -> None:
    context.tls_original = _MockOriginalStream()
    context.tls_stream = _ThreadLocalStream(context.tls_original)
    context.tls_result: Any = None
    context.tls_captured: str = ""
    context.tls_error: Exception | None = None


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


@given('the original stream has encoding "{enc}"')
def step_given_encoding(context: Context, enc: str) -> None:
    context.tls_original.encoding = enc


@given("the original stream has no encoding attribute")
def step_given_no_encoding(context: Context) -> None:
    context.tls_stream = _ThreadLocalStream(_NoEncodingStream())


@given("the original stream isatty returns True")
def step_given_isatty_true(context: Context) -> None:
    context.tls_original._isatty_return = True


@given("the original stream isatty returns False")
def step_given_isatty_false(context: Context) -> None:
    context.tls_original._isatty_return = False


@given("capture is started on the thread-local stream")
def step_given_capture_started(context: Context) -> None:
    context.tls_stream.start_capture()


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


@when("I read the thread-local stream encoding")
def step_when_read_encoding(context: Context) -> None:
    context.tls_result = context.tls_stream.encoding


@when("I check if the thread-local stream is writable")
def step_when_check_writable(context: Context) -> None:
    context.tls_result = context.tls_stream.writable()


@when("I check if the thread-local stream is readable")
def step_when_check_readable(context: Context) -> None:
    context.tls_result = context.tls_stream.readable()


@when("I flush the thread-local stream")
def step_when_flush(context: Context) -> None:
    try:
        context.tls_stream.flush()
        context.tls_error = None
    except Exception as exc:
        context.tls_error = exc


@when("I check if the thread-local stream isatty")
def step_when_check_isatty(context: Context) -> None:
    context.tls_result = context.tls_stream.isatty()


@when('I write "{text}" to the thread-local stream')
def step_when_write(context: Context, text: str) -> None:
    context.tls_stream.write(text)


@when("capture is stopped on the thread-local stream")
def step_when_stop_capture(context: Context) -> None:
    context.tls_captured = context.tls_stream.stop_capture()


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


@then('the encoding should be "{enc}"')
def step_then_encoding(context: Context, enc: str) -> None:
    assert context.tls_result == enc, (
        f"Expected encoding '{enc}', got '{context.tls_result}'"
    )


@then("writable should return True")
def step_then_writable_true(context: Context) -> None:
    assert context.tls_result is True, (
        f"Expected writable()=True, got {context.tls_result}"
    )


@then("readable should return False")
def step_then_readable_false(context: Context) -> None:
    assert context.tls_result is False, (
        f"Expected readable()=False, got {context.tls_result}"
    )


@then("the flush should succeed without error")
def step_then_flush_ok(context: Context) -> None:
    assert context.tls_error is None, f"Expected no error, got {context.tls_error}"


@then("the original stream flush should be called")
def step_then_original_flushed(context: Context) -> None:
    assert context.tls_original._flushed, (
        "Expected original stream flush() to be called"
    )


@then("isatty should return True")
def step_then_isatty_true(context: Context) -> None:
    assert context.tls_result is True, (
        f"Expected isatty()=True, got {context.tls_result}"
    )


@then("isatty should return False")
def step_then_isatty_false(context: Context) -> None:
    assert context.tls_result is False, (
        f"Expected isatty()=False, got {context.tls_result}"
    )


@then('the original stream should have received "{text}"')
def step_then_original_received(context: Context, text: str) -> None:
    assert text in context.tls_original._written, (
        f"Expected '{text}' in original writes, got {context.tls_original._written}"
    )


@then('the captured text should be "{text}"')
def step_then_captured_text(context: Context, text: str) -> None:
    assert context.tls_captured == text, (
        f"Expected captured text '{text}', got '{context.tls_captured}'"
    )


@then("the captured text should be empty")
def step_then_captured_text_empty(context: Context) -> None:
    assert context.tls_captured == "", (
        f"Expected empty captured text, got '{context.tls_captured}'"
    )
