#!/usr/bin/env python3
#
# SPDX-FileCopyrightText: 2026 Kienan Stewart <kstewart@efficios.com>
# SPDX-License-Identifier: GPL-2.0-only
#

import logging
import os
import pathlib
import shutil
import subprocess
import tempfile
import typing


def gdb_exists() -> bool:
    """Return True if GDB can be executed."""
    return shutil.which("gdb") is not None


def gdb_script(
    gdb_commands: typing.List[str],
    subprocess_kwargs: dict = dict(),
    breakpoint_pending: str = "on",
    pagination: str = "off",
    debug: bool = False,
) -> typing.Tuple[subprocess.Popen, tempfile.NamedTemporaryFile]:
    """
    Runs GDB commands as a batch script in a subprocess.

    Returns a tuple (subprocess object, tempfile). This is done
    since the tempfile needs to stay referenced until GDB is
    done, otherwise it may be deleted.
    """
    pre = [
        "set breakpoint pending {}".format(breakpoint_pending),
        "set pagination {}".format(pagination),
    ]
    if debug:
        # echo each command before it runs.
        echoed = []
        for command in gdb_commands:
            echoed.append("echo + {}\\n".format(command))
            echoed.append(command)

        gdb_commands = echoed

    commands = pre + gdb_commands
    script = tempfile.NamedTemporaryFile(
        prefix="gdb_",
    )
    logging.info("GDB script contents:")
    for command in commands:
        logging.info("  {}".format(command))

    # While NamedTemporaryFile() already returns a value that
    # can be used with write() without opening again, doing a
    # second open allows the context wrapper to perform a flush
    # and fsync before the subprocess is invoked.
    with open(script.name, "w") as f:
        f.write("\n".join(commands))

    gdb_args = ["gdb", "--nx", "--nw", "--batch", "-x", script.name]
    p = subprocess.Popen(gdb_args, **subprocess_kwargs)
    return (p, script)


def get_logging_format(tap: bool = True) -> str:
    return "{}[%(created)s] - %(levelname)s - %(message)s".format("# " if tap else "")


def get_pipe_max_size() -> int:
    try:
        with open("/proc/sys/fs/pipe-max-size", "r") as f:
            return int(f.read())
    except FileNotFoundError:
        # E.g. FreeBSD
        # Kernel configuration dependant, but defaults to 64 * 1024
        # https://github.com/freebsd/freebsd-src/blob/5b0dc991093c82824f6fe566af947f64f5072264/sys/sys/pipe.h#L33
        return 65536


def shutil_ignore_sockets(
    directory: str, directory_contents: typing.List[str]
) -> typing.List[str]:
    ignore = list()
    for entry in directory_contents:
        full_path = pathlib.Path(os.path.join(directory, entry))
        if full_path.is_socket():
            ignore.append(entry)

    return ignore
