"""
Script runner for Telegram bot - executes user notification scripts on demand.

Used by callback query handler when user clicks "Run" button in /status.
Runs the script as the owning user via the notify-scripts helper.
"""

import json
import logging
import re
import subprocess

from . import config

logger = logging.getLogger(__name__)

NOTIFY_SCRIPTS_BIN = "/usr/local/bin/notify-scripts"

# POSIX-conservative username shape: alphanumerics, dot, hyphen, underscore;
# must start with `[a-z_]` so the value can never be interpreted as a sudo
# flag (e.g. `-u`, `--shell`). Mirrors the `useradd` defaults. Anything
# outside this shape is refused before we hand it to `sudo -u`.
_USERNAME_RE = re.compile(r"^[a-z_][a-z0-9._-]{0,31}$")


def run_user_script(username: str, script_name: str) -> dict | None:
    """Run a notification script as the specified user and return parsed JSON output.

    Returns None on error, or the parsed JSON dict on success.
    """
    if not _USERNAME_RE.match(username):
        logger.error(f"Refusing to run script: invalid username shape: {username!r}")
        return None

    if not script_name.endswith(".py"):
        logger.warning(f"Not a Python script: {script_name}")
        return None

    try:
        result = subprocess.run(
            ["/usr/bin/sudo", "-u", username, NOTIFY_SCRIPTS_BIN, "run", script_name],
            capture_output=True,
            text=True,
            timeout=config.SCRIPT_TIMEOUT_SECONDS + 10,  # extra margin over inner timeout
        )

        if result.returncode != 0:
            # notify-scripts prints JSON error to stdout on failure
            try:
                error_info = json.loads(result.stdout)
                logger.warning(f"Script {script_name} (user={username}) failed: {error_info.get('error', 'unknown')}")
            except (json.JSONDecodeError, Exception):
                logger.warning(
                    f"Script {script_name} (user={username}) exited with code "
                    f"{result.returncode}: {result.stderr[:500]}"
                )
            return None

        stdout = result.stdout.strip()
        if not stdout:
            logger.warning(f"Script {script_name} produced no stdout")
            return None

        parsed = json.loads(stdout)
        logger.info(f"Script {script_name} output: image_path={parsed.get('image_path', 'MISSING')}")
        return parsed

    except subprocess.TimeoutExpired:
        logger.error(f"Script {script_name} timed out after {config.SCRIPT_TIMEOUT_SECONDS}s")
        return None
    except json.JSONDecodeError as e:
        logger.error(f"Script {script_name} returned invalid JSON: {e}")
        return None
    except Exception:
        logger.exception(f"Error running script {script_name} for user {username}")
        return None
