"""Fixed-purpose managed public-key reconciliation inside the single Linux guest.

No shell input, password or private key is accepted. Existing daemon settings and
unrelated authorized_keys lines are preserved. Importable for isolated tests.
"""
import base64
import fcntl
import json
import os
import pathlib
import pwd
import re
import shlex
import shutil
import stat
import subprocess
import sys
import tempfile


def validated_keys(keys):
    if not isinstance(keys, list) or len(keys) > 100:
        raise ValueError("Invalid managed-key request")
    result, seen = [], set()
    for key in keys:
        if not isinstance(key, dict) or not re.fullmatch(r"sshkey_[a-f0-9]{36}", str(key.get("id", ""))):
            raise ValueError("Invalid managed key identity")
        public = key.get("publicKey", "")
        if not isinstance(public, str) or len(public) > 8192 or not re.fullmatch(r"(ssh-ed25519|ssh-rsa|ecdsa-sha2-nistp256) [A-Za-z0-9+/]+={0,2}", public):
            raise ValueError("Only a plain SSH public key is accepted")
        kind, encoded = public.split(" ")
        wire = base64.b64decode(encoded, validate=True)
        if len(wire) < 8 or int.from_bytes(wire[:4], "big") != len(kind) or wire[4:4+len(kind)] != kind.encode():
            raise ValueError("Invalid SSH public-key content")
        if type(key.get("present")) is not bool or type(key.get("revision")) is not int or key["revision"] < 1 or key["id"] in seen:
            raise ValueError("Invalid managed-key state")
        seen.add(key["id"])
        result.append({"id": key["id"], "publicKey": public, "present": key["present"], "revision": key["revision"]})
    return result


def reconcile_text(original, keys):
    by_id = {key["id"]: key for key in validated_keys(keys)}
    kept = []
    for line in original.splitlines(keepends=True):
        marker = re.search(r"(?:^|\s)macserve-managed:(sshkey_[a-f0-9]{36})\s*$", line)
        key = by_id.get(marker.group(1)) if marker else None
        if key:
            if key["publicKey"] not in line:
                raise ValueError("A managed key was edited in Linux; review it before retrying")
        else:
            kept.append(line)
    text = "".join(kept)
    additions = [f'no-agent-forwarding,no-X11-forwarding {key["publicKey"]} macserve-managed:{key["id"]}\n' for key in keys if key["present"]]
    if additions and text and not text.endswith("\n"):
        text += "\n"
    return text + "".join(additions)


def checked_directory(path, owner):
    info = path.lstat()
    if not stat.S_ISDIR(info.st_mode) or info.st_uid not in (0, owner) or info.st_mode & 0o022:
        raise ValueError("SSH directory has unsafe ownership or permissions")


def update_authorized_keys(home, owner, group, keys):
    home = pathlib.Path(home)
    checked_directory(home, owner)
    directory = home / ".ssh"
    if not directory.exists():
        directory.mkdir(mode=0o700)
        os.chown(directory, owner, group)
    checked_directory(directory, owner)
    lock_fd = os.open(directory / ".macserve-managed.lock", os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600)
    with os.fdopen(lock_fd, "r+") as lock:
        info = os.fstat(lock.fileno())
        if not stat.S_ISREG(info.st_mode) or info.st_uid not in (0, owner) or info.st_mode & 0o077:
            raise ValueError("Unsafe SSH coordination file")
        fcntl.flock(lock, fcntl.LOCK_EX)
        target = directory / "authorized_keys"
        original = ""
        try:
            fd = os.open(target, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
        except FileNotFoundError:
            fd = None
        if fd is not None:
            with os.fdopen(fd, "r") as source:
                info = os.fstat(source.fileno())
                if not stat.S_ISREG(info.st_mode) or info.st_uid not in (0, owner) or info.st_mode & 0o022 or info.st_size > 262144:
                    raise ValueError("SSH key file has unsafe ownership, permissions or size")
                original = source.read()
        updated = reconcile_text(original, keys)
        if updated == original:
            return
        fd, temporary = tempfile.mkstemp(prefix=".macserve-keys-", dir=directory)
        try:
            with os.fdopen(fd, "w") as out:
                out.write(updated)
                out.flush()
                os.fchmod(out.fileno(), 0o600)
                os.fchown(out.fileno(), owner, group)
                os.fsync(out.fileno())
            os.replace(temporary, target)
        finally:
            if os.path.exists(temporary):
                os.unlink(temporary)


def daemon_configuration(username):
    # Inspect the actual listener, including an operator-specified config file.
    # Never start/restart sshd or replace its configuration.
    for process in pathlib.Path("/proc").iterdir():
        if not process.name.isdigit():
            continue
        try:
            executable = os.readlink(process / "exe")
            raw = (process / "cmdline").read_bytes().replace(b"\0", b" ").decode().strip()
        except (OSError, UnicodeError):
            continue
        if pathlib.Path(executable).name not in ("sshd", "sshd.pam") or "[listener]" not in raw:
            continue
        words = shlex.split(raw.split("[listener]", 1)[0])
        start = next((index for index, word in enumerate(words) if word == executable), None)
        options = []
        if start is not None:
            args, index = words[start+1:], 0
            while index < len(args):
                if args[index] in ("-f", "-o", "-h", "-p") and index+1 < len(args):
                    options.extend(args[index:index+2]); index += 2
                elif args[index] in ("-D", "-e", "-q"):
                    index += 1
                else:
                    raise ValueError("SSH uses custom startup options; review setup manually")
        result = subprocess.run([executable, *options, "-T", "-C", f"user={username},host=localhost,addr=127.0.0.1,laddr=127.0.0.1,lport=22"], capture_output=True, text=True, timeout=10, check=True)
        values = {}
        for line in result.stdout.splitlines():
            key, _, value = line.partition(" ")
            values[key] = value
        if "22" not in values.get("port", "").split() or values.get("pubkeyauthentication") != "yes":
            continue
        return values
    raise ValueError("A running key-authenticated SSH server on Linux port 22 is required")


def prepare(username, keys):
    keys = validated_keys(keys)
    if not re.fullmatch(r"[a-z_][a-z0-9_-]{0,31}", username, re.I) or username == "root":
        raise ValueError("Use the existing non-root Linux account")
    user = pwd.getpwnam(username)
    settings = daemon_configuration(username)
    accepted = [".ssh/authorized_keys", "%h/.ssh/authorized_keys", user.pw_dir + "/.ssh/authorized_keys"]
    if not any(path in accepted for path in settings.get("authorizedkeysfile", "").split()):
        raise ValueError("Custom AuthorizedKeysFile is configured; existing settings were preserved")
    scanner = shutil.which("ssh-keyscan")
    if not scanner:
        raise ValueError("The Linux OpenSSH client tools are missing; install openssh-client and retry")
    # This identity is collected inside the already-authenticated local VM,
    # never from an untrusted public endpoint on the connecting agent's network.
    scan = subprocess.run([scanner, "-T", "5", "-p", "22", "-t", "ed25519,ecdsa,rsa", "127.0.0.1"], capture_output=True, text=True, timeout=20, check=True)
    host_keys = sorted(set(" ".join(line.split()[1:3]) for line in scan.stdout.splitlines() if not line.startswith("#") and len(line.split()) >= 3))
    if not host_keys:
        raise ValueError("Could not read this Linux SSH server's identity")
    update_authorized_keys(user.pw_dir, user.pw_uid, user.pw_gid, keys)
    return {"success": True, "username": username, "hostKeys": host_keys,
            "keys": [{"id": key["id"], "revision": key["revision"], "present": key["present"]} for key in keys]}


if __name__ == "__main__":
    try:
        if len(sys.argv) != 3 or len(sys.argv[2]) > 150000:
            raise ValueError("Invalid SSH setup request")
        payload = json.loads(base64.b64decode(sys.argv[2], validate=True))
        print(json.dumps(prepare(sys.argv[1], payload["keys"])))
    except Exception:
        # Do not send daemon configuration, paths, keys or raw process output to logs.
        print("SSH preparation failed; existing daemon configuration was preserved.", file=sys.stderr)
        sys.exit(1)
