#!/usr/bin/env python3
"""
Minimal PTY bridge for ZevaiRouter web terminal.

stdin  -> PTY master (keystrokes)
stdout <- PTY master (shell output), raw binary
stderr  = control / errors (utf-8 text lines)

Optional control on a side channel is not needed: resize arrives as
OSC-like escape on stdin:  \\x1b]9999;<cols>;<rows>\\x07
"""
from __future__ import annotations

import errno
import fcntl
import os
import select
import signal
import struct
import sys
import termios


def set_winsize(fd: int, rows: int, cols: int) -> None:
    try:
        packed = struct.pack("HHHH", max(5, rows), max(20, cols), 0, 0)
        fcntl.ioctl(fd, termios.TIOCSWINSZ, packed)
    except Exception:
        pass


def main() -> int:
    cols = int(sys.argv[1]) if len(sys.argv) > 1 else 120
    rows = int(sys.argv[2]) if len(sys.argv) > 2 else 40
    shell = os.environ.get("SHELL") or ("/bin/bash" if os.path.exists("/bin/bash") else "/bin/sh")
    cwd = os.path.expanduser("~")

    pid, master = pty_fork = _fork_pty()
    if pid == 0:
        # child
        try:
            os.chdir(cwd)
        except Exception:
            pass
        os.environ.setdefault("TERM", "xterm-256color")
        os.environ.setdefault("COLORTERM", "truecolor")
        os.environ.setdefault("FORCE_COLOR", "1")
        try:
            os.execv(shell, [shell, "-il"])
        except Exception:
            os.execv("/bin/sh", ["/bin/sh", "-i"])
        return 1

    set_winsize(master, rows, cols)

    # Non-blocking master + stdin
    for fd in (master, sys.stdin.fileno()):
        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)

    # Binary stdout
    stdout = sys.stdout.buffer
    stdin = sys.stdin.buffer

    resize_buf = b""

    def handle_resize_escape(chunk: bytes) -> bytes:
        """Strip and apply embedded resize escapes, return remaining bytes."""
        nonlocal resize_buf
        data = resize_buf + chunk
        out = bytearray()
        i = 0
        while i < len(data):
            # ESC ] 9999 ; cols ; rows BEL
            if data[i] == 0x1B and data[i : i + 6] == b"\x1b]9999;":
                end = data.find(b"\x07", i + 6)
                if end < 0:
                    resize_buf = data[i:]
                    return bytes(out)
                payload = data[i + 6 : end].decode("ascii", "ignore")
                try:
                    c_s, r_s = payload.split(";", 1)
                    set_winsize(master, int(r_s), int(c_s))
                except Exception:
                    pass
                i = end + 1
                resize_buf = b""
                continue
            out.append(data[i])
            i += 1
        resize_buf = b""
        return bytes(out)

    alive = True

    def on_chld(signum, frame):  # noqa: ARG001
        nonlocal alive
        alive = False

    signal.signal(signal.SIGCHLD, on_chld)

    try:
        while alive:
            try:
                r, _, _ = select.select([master, sys.stdin.fileno()], [], [], 0.25)
            except InterruptedError:
                continue

            if master in r:
                try:
                    data = os.read(master, 8192)
                except OSError as e:
                    if e.errno in (errno.EIO, errno.EAGAIN, errno.EWOULDBLOCK):
                        data = b""
                    else:
                        break
                if not data:
                    # EIO often means child exited
                    if not alive:
                        break
                else:
                    stdout.write(data)
                    stdout.flush()

            if sys.stdin.fileno() in r:
                try:
                    data = stdin.read(8192)
                except Exception:
                    data = b""
                if data is None:
                    data = b""
                if data == b"" and not alive:
                    break
                if data:
                    cleaned = handle_resize_escape(data)
                    if cleaned:
                        try:
                            os.write(master, cleaned)
                        except OSError:
                            break
    finally:
        try:
            os.close(master)
        except Exception:
            pass
        try:
            os.waitpid(pid, 0)
        except Exception:
            pass
    return 0


def _fork_pty():
    import pty

    return pty.fork()


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except KeyboardInterrupt:
        raise SystemExit(0)
