#!/usr/bin/env python3
"""sicd — sic wire protocol daemon, reference implementation.

Reads one wire frame from stdin, peels off the outer layer, and execs
the command with the payload + trailing stdin forwarded to the child.
"""

import os
import struct
import sys


def read_exact(n: int) -> bytes:
    """Read exactly n bytes from stdin, or raise EOFError."""
    buf = b""
    while len(buf) < n:
        chunk = sys.stdin.buffer.read(n - len(buf))
        if not chunk:
            raise EOFError("unexpected EOF")
        buf += chunk
    return buf


def parse_netstring(data: bytes) -> tuple[bytes, bytes]:
    """Parse a DJB netstring from data, return (content, rest)."""
    # Find the colon separating length from content
    colon = data.find(b":")
    if colon == -1:
        raise ValueError("netstring: missing colon")
    try:
        length = int(data[:colon])
    except ValueError:
        raise ValueError("netstring: invalid length")
    content_start = colon + 1
    content_end = content_start + length
    if content_end + 1 > len(data):
        raise ValueError("netstring: truncated")
    if data[content_end:content_end+1] != b",":
        raise ValueError("netstring: missing trailing comma")
    return data[content_start:content_end], data[content_end+1:]


def main() -> int:
    try:
        # Read preamble: magic byte + 4-byte big-endian length
        preamble = read_exact(5)
        if preamble[0:1] != b"\x00":
            sys.stderr.write("sicd: missing magic byte\n")
            return 1
        ns_len = struct.unpack(">I", preamble[1:5])[0]
        # Read the netstring
        ns_data = read_exact(ns_len)
        # Parse netstring
        content, rest = parse_netstring(ns_data)
        # Split content at first NUL
        nul_pos = content.find(b"\x00")
        if nul_pos == -1:
            sys.stderr.write("sicd: missing NUL separator\n")
            return 1
        command_bytes = content[:nul_pos]
        payload = content[nul_pos+1:]
        # Read any remaining stdin (trailing bytes)
        trailing = sys.stdin.buffer.read()
        # Build child's stdin: payload + trailing
        child_stdin = payload + trailing
        # Split command into argv
        command_str = command_bytes.decode("ascii", errors="replace")
        argv = command_str.split()
        if not argv:
            sys.stderr.write("sicd: empty command\n")
            return 1
        # Fork and exec
        pid = os.fork()
        if pid == 0:
            # Child
            # Redirect stdin to pipe with child_stdin
            r, w = os.pipe()
            os.write(w, child_stdin)
            os.close(w)
            os.dup2(r, 0)
            os.close(r)
            os.execvp(argv[0], argv)
            # If exec fails
            sys.stderr.write(f"sicd: exec failed: {argv[0]}\n")
            os._exit(1)
        else:
            # Parent
            _, status = os.waitpid(pid, 0)
            if os.WIFEXITED(status):
                return os.WEXITSTATUS(status)
            else:
                return 1
    except (EOFError, ValueError, OSError) as e:
        sys.stderr.write(f"sicd: {e}\n")
        return 1


if __name__ == "__main__":
    sys.exit(main())
