"""Serialise an EnvBuilder to .env on disk.

The output format is the docker-compose `.env`-subset that is also
sourceable from bash (via `set -a; source .env; set +a`). Values that
contain only "safe" characters are emitted bare; everything else is
double-quoted with backslash-escaping of `\\` and `"`.

Each key is preceded by its documentation comment when the builder
carries one, mirroring the convention used in `default.env`.
"""

from __future__ import annotations

import os
import re
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from pathlib import Path

    from utils.env.builder import EnvBuilder

_SAFE_VALUE_RE = re.compile(r"^[A-Za-z0-9_/.@:%+=-]+$")


def quote_for_dotenv(value: str) -> str:
    if value != "" and _SAFE_VALUE_RE.match(value):
        return value
    if "\n" in value or "\r" in value:
        raise ValueError(
            "Multi-line env values cannot be written to .env "
            "(bash `source` and docker-compose `--env-file` both fail "
            "to round-trip them). Collapse to a single line at the "
            f"producer. Offending value: {value!r}"
        )
    escaped = value.replace("\\", "\\\\").replace('"', '\\"')
    return f'"{escaped}"'


def write_dotenv(eb: EnvBuilder, dest: Path) -> None:
    lines: list[str] = [
        "# Generated by `make dotenv` (python -m cli.meta.env) -- do NOT edit.",
        "# Re-run after changes to default.env or after the runtime context",
        "# shifts (distro, CI vs local, fresh checkout).",
    ]
    for key in sorted(eb.values):
        lines.append("")
        comment = eb.comments.get(key, "").strip()
        if comment:
            lines.append(f"# {comment}")
        lines.append(f"{key}={quote_for_dotenv(eb.values[key])}")
    tmp = dest.with_name(f"{dest.name}.{os.getpid()}.tmp")
    try:
        tmp.write_text("\n".join(lines) + "\n", encoding="utf-8")
        tmp.replace(dest)
    except BaseException:
        tmp.unlink(missing_ok=True)
        raise
