"""pytest plugin — run pyos UI tests in headful mode.

Activate with:
    pytest --headful
    pytest --headful tests/test_timeline.py -k scroll
    FRAME_DELAY=0.1 pytest --headful

Any project that installs pyos gets this flag automatically via the
pytest11 entry point.
"""

import os
import sys
import time
import curses

from pyos.testing.harness import MockScreen


# ── ANSI attribute mapping ────────────────────────────────────────────

def _ansi(attr):
    codes = []
    if attr & curses.A_BOLD:      codes.append("1")
    if attr & curses.A_DIM:       codes.append("2")
    if attr & curses.A_UNDERLINE: codes.append("4")
    if attr & curses.A_REVERSE:   codes.append("7")
    return f"\033[{';'.join(codes)}m" if codes else "\033[0m"


# ── Terminal renderer ─────────────────────────────────────────────────

class Renderer:
    """Renders MockScreen buffers to the real terminal via ANSI escapes."""

    def __init__(self, delay=0.04):
        self.delay = delay
        self.test_name = ""
        self.passed = 0
        self.failed = 0
        self._tty = open("/dev/tty", "w")

    def enter(self):
        """Alt screen, hide cursor, clear."""
        self._write("\033[?1049h\033[?25l\033[2J")

    def leave(self):
        """Restore terminal."""
        self._write("\033[?1049l\033[?25h")
        self._tty.close()

    def _write(self, s):
        self._tty.write(s)
        self._tty.flush()

    def render(self, screen):
        buf = []
        w = screen.cols

        # cursor home
        buf.append("\033[H")

        # ── title bar ──
        name = self.test_name
        stats = f" {self.passed}\u2713 {self.failed}\u2717 "
        space = w + 2 - len(stats)
        if len(name) + 2 > space:
            name = name[: space - 5] + "\u2026"
        pad = max(0, space - len(name) - 1)
        buf.append(f"\033[1;97;44m {name}{' ' * pad}{stats}\033[0m\033[K\n")

        # ── top border ──
        buf.append(f"\033[36m\u250c{'\u2500' * w}\u2510\033[0m\033[K\n")

        # ── screen rows ──
        for r in range(screen.rows):
            buf.append("\033[36m\u2502\033[0m")
            prev = -1
            for c in range(w):
                a = screen.attr_buffer[r][c]
                if a != prev:
                    buf.append(_ansi(a))
                    prev = a
                buf.append(screen.buffer[r][c])
            buf.append("\033[0m\033[36m\u2502\033[0m\033[K\n")

        # ── bottom border ──
        buf.append(f"\033[36m\u2514{'\u2500' * w}\u2518\033[0m\033[K\n")

        # clear anything below (handles screen-size changes between tests)
        buf.append("\033[J")

        self._tty.write("".join(buf))
        self._tty.flush()

        if self.delay > 0:
            time.sleep(self.delay)


# ── Monkeypatch plumbing ─────────────────────────────────────────────

_renderer = None
_orig_refresh = MockScreen.refresh


def _visual_refresh(self):
    _orig_refresh(self)
    if _renderer is not None:
        _renderer.render(self)


# ── Pytest hooks ─────────────────────────────────────────────────────

def pytest_addoption(parser):
    parser.addoption(
        "--headful",
        action="store_true",
        default=False,
        help="Render MockScreen to the terminal in real time.",
    )


def pytest_configure(config):
    global _renderer

    if not config.getoption("headful", default=False):
        return

    delay = float(os.environ.get("FRAME_DELAY", "0.04"))
    _renderer = Renderer(delay=delay)
    MockScreen.refresh = _visual_refresh
    _renderer.enter()

    config._headful_renderer = _renderer
    config.pluginmanager.register(_HeadfulReporter(_renderer), "headful-reporter")


def pytest_unconfigure(config):
    global _renderer

    renderer = getattr(config, "_headful_renderer", None)
    if renderer is None:
        return

    time.sleep(0.3)
    _renderer = None
    MockScreen.refresh = _orig_refresh
    renderer.leave()

    reporter = config.pluginmanager.get_plugin("headful-reporter")
    if reporter:
        _print_summary(reporter)


class _HeadfulReporter:
    """Tracks pass/fail and updates the title bar during headful runs."""

    def __init__(self, renderer):
        self.renderer = renderer
        self.failures = []

    def pytest_runtest_logstart(self, nodeid, location):
        self.renderer.test_name = nodeid

    def pytest_runtest_logreport(self, report):
        if report.when == "call":
            if report.passed:
                self.renderer.passed += 1
            elif report.failed:
                self.renderer.failed += 1
                text = getattr(report, "longreprtext", "") or ""
                self.failures.append((report.nodeid, text))


def _print_summary(reporter):
    p = reporter.renderer.passed
    f = reporter.renderer.failed
    print()
    if f == 0:
        print(f"\033[32m \u2713 {p} passed\033[0m")
    else:
        print(f"\033[32m \u2713 {p} passed\033[0m  \033[31m\u2717 {f} failed\033[0m")
        for nodeid, text in reporter.failures:
            print(f"\n\033[31m  FAIL {nodeid}\033[0m")
            for line in text.strip().splitlines()[-4:]:
                print(f"    {line}")
    print()
