"""
Utilities for safely reading and writing ``.env`` files.

We avoid using ``dotenv.set_key`` directly because it has edge-cases with
quoted values and comments.  Instead, we maintain the existing file structure
(preserving comments and blank lines) and only update or append the target key.
"""

from __future__ import annotations

import logging
import re
import shutil
from datetime import datetime, timezone
from pathlib import Path

logger = logging.getLogger(__name__)

# Regex matching a KEY=VALUE assignment (with optional export keyword and quotes).
_KEY_LINE_RE = re.compile(
    r"""^(?P<export>export\s+)?(?P<key>[A-Za-z_][A-Za-z0-9_]*)(?P<rest>=.*)$"""
)


def upsert_key(path: str | Path, key: str, value: str) -> None:
    """Insert or update a key in a ``.env`` file, preserving formatting.

    If the file does not exist it is created.  If the key already exists on any
    line, that line is updated in-place.  Otherwise the key-value pair is
    appended at the end of the file.

    Args:
        path: Path to the ``.env`` file.
        key: Environment variable name (must match ``[A-Za-z_][A-Za-z0-9_]*``).
        value: New value.  If the value contains spaces or special characters it
            will be double-quoted automatically.

    Raises:
        ValueError: If *key* is not a valid identifier.
    """
    path = Path(path)
    _validate_key(key)
    quoted_value = _quote_if_needed(value)
    new_line = f"{key}={quoted_value}\n"

    if not path.exists():
        path.touch()
        logger.debug("Created new .env file at '%s'.", path)

    lines = path.read_text(encoding="utf-8").splitlines(keepends=True)
    updated = False

    for idx, line in enumerate(lines):
        match = _KEY_LINE_RE.match(line)
        if match and match.group("key") == key:
            prefix = match.group("export") or ""
            lines[idx] = f"{prefix}{new_line}"
            updated = True
            logger.debug("Updated key '%s' in '%s'.", key, path)
            break

    if not updated:
        if lines and not lines[-1].endswith("\n"):
            lines.append("\n")
        lines.append(new_line)
        logger.debug("Appended key '%s' to '%s'.", key, path)

    _write_atomically(path, "".join(lines))


def delete_key(path: str | Path, key: str) -> bool:
    """Remove a key from a ``.env`` file.

    Args:
        path: Path to the ``.env`` file.
        key: Key to remove.

    Returns:
        ``True`` if the key was found and removed, ``False`` otherwise.
    """
    path = Path(path)
    _validate_key(key)

    if not path.exists():
        return False

    lines = path.read_text(encoding="utf-8").splitlines(keepends=True)
    new_lines = []
    removed = False

    for line in lines:
        match = _KEY_LINE_RE.match(line)
        if match and match.group("key") == key:
            removed = True
            logger.debug("Removed key '%s' from '%s'.", key, path)
        else:
            new_lines.append(line)

    if removed:
        _write_atomically(path, "".join(new_lines))

    return removed


def create_env_template(
    path: str | Path,
    keys: list[str],
    *,
    overwrite: bool = False,
) -> None:
    """Write a commented-out .env template with the given keys.

    Args:
        path: Destination ``.env`` file.
        keys: List of environment variable names to include.
        overwrite: If ``False`` (default), raise if the file already exists.

    Raises:
        FileExistsError: If *path* exists and *overwrite* is ``False``.
    """
    path = Path(path)

    if path.exists() and not overwrite:
        raise FileExistsError(
            f"'{path}' already exists.  Use overwrite=True to replace it."
        )

    now = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
    lines: list[str] = [
        f"# Generated by SafeConfig on {now}\n",
        "# Uncomment and fill in each variable.\n",
        "#\n",
        "# IMPORTANT: Never commit this file to version control!\n",
        "\n",
    ]

    for key in keys:
        lines.append(f"# {key}=\n")

    path.write_text("".join(lines), encoding="utf-8")
    logger.info("Created .env template at '%s' with %d key(s).", path, len(keys))


# ─── Private helpers ─────────────────────────────────────────────────────────


def _validate_key(key: str) -> None:
    if not re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", key):
        raise ValueError(
            f"'{key}' is not a valid environment variable name.  "
            "Use only letters, digits, and underscores, starting with a letter or underscore."
        )


def _quote_if_needed(value: str) -> str:
    """Surround *value* with double quotes if it contains problematic characters."""
    # Characters that require quoting in .env files
    if any(c in value for c in (' ', '\t', '"', "'", '#', '$', '\\', '`', '!')):
        escaped = value.replace("\\", "\\\\").replace('"', '\\"')
        return f'"{escaped}"'
    return value


def _write_atomically(path: Path, content: str) -> None:
    """Write *content* to *path* via a temporary file to prevent corruption."""
    tmp = path.with_suffix(".tmp")
    try:
        tmp.write_text(content, encoding="utf-8")
        shutil.move(str(tmp), str(path))
    except Exception:
        if tmp.exists():
            tmp.unlink()
        raise
