#!/usr/bin/env python3
from __future__ import annotations

import argparse
import sys
from pathlib import Path
from typing import Any

try:
    import yaml
except ImportError as exc:  # pragma: no cover
    raise SystemExit(
        "Missing dependency: PyYAML. Install it with `python -m pip install pyyaml`."
    ) from exc


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Render docker compose .env from deploy config.yaml"
    )
    parser.add_argument(
        "--input",
        default="deploy/docker/config.yaml",
        help="Path to config.yaml (default: deploy/docker/config.yaml)",
    )
    parser.add_argument(
        "--output",
        default="deploy/docker/.env",
        help="Output dotenv file path (default: deploy/docker/.env)",
    )
    parser.add_argument(
        "--section",
        default="compose_env",
        help="Top-level mapping section to export (default: compose_env)",
    )
    parser.add_argument(
        "--stdout",
        action="store_true",
        help="Print rendered dotenv to stdout instead of writing file",
    )
    return parser.parse_args()


def load_config(path: Path) -> dict[str, Any]:
    if not path.exists():
        raise SystemExit(f"Config file not found: {path}")

    data = yaml.safe_load(path.read_text(encoding="utf-8"))
    if not isinstance(data, dict):
        raise SystemExit("config.yaml root must be a mapping/object")
    return data


def encode_env_value(value: Any) -> str:
    if value is None:
        return '""'
    if isinstance(value, bool):
        return "true" if value else "false"
    if isinstance(value, (int, float)):
        return str(value)
    if not isinstance(value, str):
        raise SystemExit(f"compose_env only supports scalar values, got: {type(value).__name__}")

    if value == "":
        return '""'

    needs_quotes = any(ch in value for ch in [' ', '#', '"', "'", '\t', '\n', '\r']) or value.startswith('$')
    if not needs_quotes:
        return value

    escaped = (
        value.replace('\\', '\\\\')
        .replace('"', '\\"')
        .replace('\n', '\\n')
        .replace('\r', '\\r')
        .replace('\t', '\\t')
    )
    return f'"{escaped}"'


def render_env(section_name: str, values: dict[str, Any], source_path: Path) -> str:
    lines = [
        f"# Generated from {source_path.as_posix()}::{section_name}",
        "# Do not edit this file directly; edit config.yaml and re-render.",
        "",
    ]

    for key, value in values.items():
        if not isinstance(key, str) or not key:
            raise SystemExit(f"Invalid env key: {key!r}")
        lines.append(f"{key}={encode_env_value(value)}")

    lines.append("")
    return "\n".join(lines)


def main() -> int:
    args = parse_args()
    source_path = Path(args.input)
    output_path = Path(args.output)

    data = load_config(source_path)
    section = data.get(args.section)
    if not isinstance(section, dict):
        raise SystemExit(
            f"Section `{args.section}` must exist in config.yaml and must be a mapping/object"
        )

    rendered = render_env(args.section, section, source_path)

    if args.stdout:
        sys.stdout.write(rendered)
        return 0

    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text(rendered, encoding="utf-8", newline="\n")
    print(f"Wrote {output_path}")
    return 0


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