#!/usr/bin/env python3
"""Local TRAPPIST-1 worker intended for Atavism/PHP-gated invocation.

This script is not a public API server. It is a narrow local worker that:
1. returns the current canonical TRAPPIST-1 client payload,
2. returns the raw NASA snapshot when explicitly requested, or
3. refreshes the local NASA-derived files by invoking the approved fetch script.

The intended deployment model is:
1. Unity client talks to Atavism only.
2. Atavism validates the player action.
3. PHP or another local gatekeeper validates the server-to-server request.
4. The gatekeeper invokes this script locally with a fixed action.

No user-supplied script names or arbitrary shell commands are accepted here.
"""

from __future__ import annotations

import argparse
import json
import subprocess
import sys
from dataclasses import dataclass
from datetime import UTC, datetime
from pathlib import Path
from typing import Any


DEFAULT_DATA_DIR = Path("Code/NASA")
DEFAULT_FETCH_SCRIPT = Path(__file__).resolve().parent / "query_nasa_db.py"


@dataclass(frozen=True)
class WorkerConfig:
    """Parsed worker configuration."""

    action: str
    data_dir: Path
    fetch_script: Path
    hostname: str
    timeout_seconds: float
    pretty: bool


def parse_args() -> WorkerConfig:
    """Parse worker command-line arguments."""

    parser = argparse.ArgumentParser(
        description=(
            "Local TRAPPIST-1 worker for trusted Atavism/PHP invocation. "
            "Outputs JSON only."
        )
    )
    parser.add_argument(
        "action",
        choices=("get-system", "get-raw", "refresh-system"),
        help="Fixed worker action to perform.",
    )
    parser.add_argument(
        "--data-dir",
        default=str(DEFAULT_DATA_DIR),
        help="Directory containing the TRAPPIST-1 JSON files.",
    )
    parser.add_argument(
        "--fetch-script",
        default=str(DEFAULT_FETCH_SCRIPT),
        help="Path to the local NASA refresh script.",
    )
    parser.add_argument(
        "--hostname",
        default="TRAPPIST-1",
        help="Host name to pass to the refresh script.",
    )
    parser.add_argument(
        "--timeout-seconds",
        type=float,
        default=30.0,
        help="Timeout to pass through to the refresh script.",
    )
    parser.add_argument(
        "--pretty",
        action="store_true",
        help="Pretty-print the JSON response for inspection.",
    )
    args = parser.parse_args()
    return WorkerConfig(
        action=args.action,
        data_dir=Path(args.data_dir),
        fetch_script=Path(args.fetch_script),
        hostname=args.hostname,
        timeout_seconds=args.timeout_seconds,
        pretty=args.pretty,
    )


def now_utc_iso() -> str:
    """Return the current UTC timestamp in ISO 8601 format."""

    return datetime.now(UTC).replace(microsecond=0).isoformat()


def load_json(path: Path) -> Any:
    """Load a JSON file from disk."""

    return json.loads(path.read_text(encoding="utf-8"))


def emit_json(payload: dict[str, Any], pretty: bool) -> None:
    """Write a JSON payload to stdout."""

    if pretty:
        sys.stdout.write(json.dumps(payload, indent=2, sort_keys=True) + "\n")
    else:
        sys.stdout.write(json.dumps(payload, separators=(",", ":"), sort_keys=True) + "\n")


def build_success_response(action: str, data: Any) -> dict[str, Any]:
    """Wrap a successful worker result in a consistent JSON envelope."""

    return {
        "ok": True,
        "action": action,
        "generated_at_utc": now_utc_iso(),
        "data": data,
    }


def build_error_response(action: str, message: str) -> dict[str, Any]:
    """Wrap a worker failure in a consistent JSON envelope."""

    return {
        "ok": False,
        "action": action,
        "generated_at_utc": now_utc_iso(),
        "error": message,
    }


def get_system_payload(data_dir: Path) -> dict[str, Any]:
    """Return the canonical client-facing TRAPPIST-1 payload."""

    return load_json(data_dir / "canonical-system.json")


def get_raw_payload(data_dir: Path) -> dict[str, Any]:
    """Return the raw NASA snapshot."""

    return load_json(data_dir / "raw-pscomppars.json")


def run_refresh(config: WorkerConfig) -> dict[str, Any]:
    """Run the local NASA refresh script and return the refreshed payload."""

    command = [
        sys.executable,
        str(config.fetch_script),
        "--hostname",
        config.hostname,
        "--output-dir",
        str(config.data_dir),
        "--timeout-seconds",
        str(config.timeout_seconds),
    ]
    completed = subprocess.run(
        command,
        check=False,
        capture_output=True,
        text=True,
    )
    if completed.returncode != 0:
        stderr_text = completed.stderr.strip()
        stdout_text = completed.stdout.strip()
        detail = stderr_text or stdout_text or f"Refresh script exited with code {completed.returncode}."
        raise RuntimeError(detail)

    return {
        "refresh_stdout": completed.stdout.strip(),
        "system": get_system_payload(config.data_dir),
    }


def handle_action(config: WorkerConfig) -> dict[str, Any]:
    """Dispatch the fixed worker action."""

    if config.action == "get-system":
        return build_success_response(config.action, get_system_payload(config.data_dir))
    if config.action == "get-raw":
        return build_success_response(config.action, get_raw_payload(config.data_dir))
    if config.action == "refresh-system":
        return build_success_response(config.action, run_refresh(config))
    raise ValueError(f"Unsupported action '{config.action}'.")


def main() -> int:
    """Run the worker and emit a JSON response."""

    config = parse_args()
    try:
        payload = handle_action(config)
        emit_json(payload, pretty=config.pretty)
        return 0
    except Exception as exc:  # noqa: BLE001
        emit_json(build_error_response(config.action, str(exc)), config.pretty)
        return 1


if __name__ == "__main__":
    raise SystemExit(main())
