# app_terminal.py - Terminal pane: queue, tee, logging handler, install. No import of app_pyobjc2.
# The app calls install_terminal_output() at launch and drains get_queue() on the main thread.
from __future__ import annotations

import logging
import sys
from datetime import datetime
from queue import Queue, Empty

# Terminal output: thread-safe queue; drained on main thread into the Terminal pane.
TERMINAL_LOG_QUEUE_MAX = 10000
TERMINAL_LOG_CAP = 500000  # max characters per terminal text view; trim from start when exceeded

_terminal_log_queue = Queue(maxsize=TERMINAL_LOG_QUEUE_MAX)
_terminal_original_stdout = None
_terminal_original_stderr = None


def get_queue():
    """Return the terminal log queue for the main thread to drain."""
    return _terminal_log_queue


class TerminalTee:
    """File-like that writes to original stream and enqueues lines for the Terminal pane."""
    def __init__(self, stream, prefix=""):
        self._stream = stream
        self._prefix = prefix
        self._buf = ""

    def write(self, s):
        if s:
            try:
                self._stream.write(s)
                self._stream.flush()
            except Exception:
                pass
            self._buf += s
            while "\n" in self._buf or "\r" in self._buf:
                line, self._buf = self._buf.split("\n", 1) if "\n" in self._buf else self._buf.split("\r", 1)
                line = (self._prefix + line).strip()
                try:
                    _terminal_log_queue.put_nowait(line + "\n")
                except Exception:
                    pass

    def flush(self):
        try:
            self._stream.flush()
        except Exception:
            pass
        if self._buf.strip():
            try:
                _terminal_log_queue.put_nowait((self._prefix + self._buf).strip() + "\n")
            except Exception:
                pass
            self._buf = ""

    def isatty(self):
        """Uvicorn and other libs call this; we are not a TTY (pipe/tee)."""
        return getattr(self._stream, "isatty", lambda: False)()

    def fileno(self):
        """Delegate to the real stream so subprocess/MCP stdio (e.g. anyio.open_process stderr=) can obtain an FD."""
        return self._stream.fileno()


class TerminalLogHandler(logging.Handler):
    def emit(self, record):
        try:
            msg = self.format(record)
            _terminal_log_queue.put_nowait(msg + "\n")
        except Exception:
            pass


def terminal_logger_callback(msg, level="INFO"):
    """Enqueue a formatted line to the terminal pane. Used by set_terminal_logger so tools show [INFO] etc."""
    try:
        ts = datetime.now().strftime("%H:%M:%S")
        level = (level or "INFO").upper()
        if level not in ("DEBUG", "INFO", "WARN", "ERROR", "TOOL", "AGENT", "TRACE", "PIPELINE"):
            level = "INFO"
        line = f"[{ts}] [{level}] {msg}\n"
        _terminal_log_queue.put_nowait(line)
    except Exception:
        pass


def terminal_lifecycle_log(message, level="INFO"):
    """Enqueue a single line to the terminal pane for app lifecycle (startup, agent run start/end)."""
    try:
        ts = datetime.now().strftime("%H:%M:%S")
        level = (level or "INFO").upper()
        if level not in ("DEBUG", "INFO", "WARN", "ERROR"):
            level = "INFO"
        line = f"[{ts}] [{level}] {message}\n"
        _terminal_log_queue.put_nowait(line)
    except Exception:
        pass


def install_terminal_output():
    """Replace stdout/stderr with tees and add a logging handler. Call once at app launch."""
    global _terminal_original_stdout, _terminal_original_stderr
    if _terminal_original_stdout is not None:
        return
    _terminal_original_stdout = sys.stdout
    _terminal_original_stderr = sys.stderr
    sys.stdout = TerminalTee(_terminal_original_stdout)
    sys.stderr = TerminalTee(_terminal_original_stderr, prefix="[stderr] ")
    root = logging.getLogger()
    h = TerminalLogHandler()
    h.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s"))
    root.addHandler(h)
