#!/usr/bin/env python3
"""Bound private stdin transport and atomically install normalized backend dotenv."""
import hashlib
import json
import os
from pathlib import Path
import sys
import tempfile

from runtime_env_parse import MAX_INPUT_BYTES, RuntimeEnvError, normalize, reject_constant, unique_pairs

MAX_ENVELOPE_BYTES = MAX_INPUT_BYTES * 6 + 1024
SCHEMA = "gowalk-cicd/runtime-env.v1"


def export_runtime():
    content = normalize(os.environ.get("RUNTIME_ENV", ""))
    digest = hashlib.sha256(content.encode("utf-8")).hexdigest()
    payload = json.dumps({"schema": SCHEMA, "content": content, "sha256": digest})
    sys.stdout.write(payload + "\n")


def receive_runtime(path):
    raw = sys.stdin.buffer.read(MAX_ENVELOPE_BYTES + 1)
    if len(raw) > MAX_ENVELOPE_BYTES:
        raise RuntimeEnvError("runtime_env_input_limit")
    try:
        payload = json.loads(raw.decode("utf-8"), object_pairs_hook=unique_pairs, parse_constant=reject_constant)
    except (ValueError, UnicodeError, RecursionError):
        raise RuntimeEnvError("runtime_env_transport_invalid") from None
    if (not isinstance(payload, dict) or set(payload) != {"schema", "content", "sha256"}
            or payload["schema"] != SCHEMA or not isinstance(payload["content"], str)):
        raise RuntimeEnvError("runtime_env_transport_invalid")
    content = normalize(payload["content"])
    if content != payload["content"] or hashlib.sha256(content.encode("utf-8")).hexdigest() != payload["sha256"]:
        raise RuntimeEnvError("runtime_env_transport_invalid")
    install_private(Path(path), content)


def install_private(destination, content):
    staging = None
    try:
        descriptor, staging = tempfile.mkstemp(prefix=".runtime-env-", dir=str(destination.parent))
        with os.fdopen(descriptor, "w", encoding="utf-8", newline="") as handle:
            os.fchmod(handle.fileno(), 0o600)
            handle.write(content)
            handle.flush()
            os.fsync(handle.fileno())
        os.replace(staging, destination)
    finally:
        if staging is not None:
            try:
                os.unlink(staging)
            except FileNotFoundError:
                pass


def main():
    try:
        if sys.argv[1:] == ["export"]:
            export_runtime()
        elif len(sys.argv) == 3 and sys.argv[1] == "receive":
            receive_runtime(sys.argv[2])
        else:
            raise RuntimeEnvError("runtime_env_command_invalid")
    except RuntimeEnvError as error:
        sys.stderr.write(str(error) + "\n")
        return 1
    except (OSError, UnicodeError):
        sys.stderr.write("runtime_env_io_failed\n")
        return 1
    return 0


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