# Copyright 2024 sysmocom - s.f.m.c. GmbH
# SPDX-License-Identifier: GPL-3.0-or-later
import datetime
import fnmatch
import glob
import json
import logging
import re
import shlex
import shutil
import subprocess
import testenv
import testenv.daemons
import testenv.testdir

core_path = None
executable_path = None


def executable_is_relevant(exe):
    if testenv.args.binary_repo:
        patterns = [
            "*/usr/bin/open5gs-*",
            "*/usr/bin/osmo-*",
        ]

        for pattern in patterns:
            if fnmatch.fnmatch(exe, pattern):
                return True
    else:
        if exe.startswith(testenv.args.cache):
            return True

    return False


def get_from_coredumpctl():
    global core_path
    global executable_path

    logging.info("Looking for a coredump with coredumpctl")

    if not shutil.which("coredumpctl"):
        logging.debug("coredumpctl is not available, won't try to get coredump")
        return

    # Check for any coredump within last 3 seconds
    since = (datetime.datetime.now() - datetime.timedelta(seconds=3)).strftime("%Y-%m-%d %H:%M:%S")
    cmd = ["coredumpctl", "-q", "-S", since, "--json=short", "-n1"]
    logging.debug(f"+ {cmd}")

    p = subprocess.run(cmd, capture_output=True, text=True)
    if p.returncode != 0:
        logging.debug("No coredump found")
        return

    # Check if the coredump executable is from osmo-*, open5gs-*, etc.
    coredump = json.loads(p.stdout)[0]
    if not executable_is_relevant(coredump["exe"]):
        logging.debug("Found an unrelated coredump, ignoring")
        return

    logging.debug("Coredump found, copying to log dir")
    core_path = f"{testenv.testdir.testdir}/core"
    testenv.cmd.run(
        ["coredumpctl", "dump", "-q", "-S", since, "-o", core_path, str(coredump["pid"]), coredump["exe"]],
        stdout=subprocess.DEVNULL,
        no_podman=True,
    )

    executable_path = coredump["exe"]


def get_from_file():
    global core_path
    global executable_path

    logging.info("Looking for a coredump file")

    glob_matches = glob.glob(f"{testenv.testdir.testdir}/*/core*")
    if not glob_matches:
        return

    core_path = glob_matches[0]
    p = testenv.cmd.run(
        ["file", core_path],
        no_podman=True,
        capture_output=True,
        text=True,
    )
    print(p.stdout, end="")

    execfn_match = re.search(r"execfn: '(.*?)'", p.stdout)
    if execfn_match:
        executable_path = execfn_match.group(1)
        logging.debug("Coredump file and execfn found")
    else:
        logging.debug("Failed to get execfn path from coredump file")


def get_coredump():
    with open("/proc/sys/kernel/core_pattern") as f:
        pattern = f.readline().rstrip()

    if "systemd-coredump" in pattern:
        get_from_coredumpctl()
    elif pattern.startswith("core"):
        get_from_file()
    else:
        logging.debug(f"Unsupported core_pattern, won't try to get coredump: {pattern}")


def get_backtrace():
    global executable_path

    if not executable_path or not core_path:
        return

    logging.info("Running gdb to get a backtrace")

    cmd = "echo; gdb"
    cmd += " --batch"
    cmd += f" {shlex.quote(executable_path)}"
    cmd += f" {shlex.quote(core_path)}"
    cmd += " -ex bt"
    cmd += f" | tee {shlex.quote(core_path)}.backtrace;"
    cmd += " echo"

    testenv.cmd.run(cmd)

    executable_path = None
