"""Output streaming handler for real-time task output processing."""

import sys
import threading
from collections.abc import Callable
from io import StringIO
from typing import Any
from hil_testbench.run.task_logger import TaskLogger


class OutputStreamer:
    """Handles real-time output streaming with callbacks and logging."""

    def __init__(
        self,
        task_name: str,
        callback: Callable[[str, bool], None] | Callable[..., Any] | None = None,
        logger: TaskLogger | None = None,
        log_output: bool | None = None,
        sample_lines: int = 0,
    ):
        self.task_name = task_name
        self.callback = callback
        self.logger = logger
        self.stdout_buffer = StringIO()
        self.stderr_buffer = StringIO()
        self._stop_event = threading.Event()
        # Logging policy: None => auto (don't log raw if callback present)
        self._log_output = log_output
        self._sample_lines = max(0, sample_lines)
        self._lines_logged = 0
        # Stats
        self.total_lines = 0
        self.total_stderr_lines = 0
        self.total_bytes = 0
        self.callback_calls = 0

    def process_line(self, line: str, is_error: bool = False):
        """Process a single line of output."""
        if line:
            # Store in buffer
            if is_error:
                self.stderr_buffer.write(line + "\n")
            else:
                self.stdout_buffer.write(line + "\n")

            # Update stats
            self.total_lines += 1
            if is_error:
                self.total_stderr_lines += 1
            self.total_bytes += len(line) + 1  # include newline

            # Decide whether to log raw output
            effective_log = (
                True
                if self._log_output is True
                else (
                    False if self._log_output is False else (self.callback is None)
                )  # auto: only if no callback attached
            )
            if self.logger and (
                effective_log
                or (self._sample_lines and self._lines_logged < self._sample_lines)
            ):
                self.logger.log_output(self.task_name, line, is_error)
                self._lines_logged += 1

            # Call user callback if provided
            if self.callback:
                try:
                    self.callback(line, is_error)
                    self.callback_calls += 1
                except Exception as e:  # pylint: disable=broad-except
                    if self.logger:
                        self.logger.error(f"Callback error: {e}", task=self.task_name)
                    else:
                        # Fallback minimal stderr if no logger available
                        sys.stderr.write(f"Callback error [{self.task_name}]: {e}\n")

    def get_stdout(self) -> str:
        """Get accumulated stdout."""
        return self.stdout_buffer.getvalue()

    def get_stderr(self) -> str:
        """Get accumulated stderr."""
        return self.stderr_buffer.getvalue()

    def stop(self):
        """Signal to stop streaming."""
        self._stop_event.set()

    def should_stop(self) -> bool:
        """Check if streaming should stop."""
        return self._stop_event.is_set()
