from __future__ import annotations

import asyncio
from datetime import datetime
from pathlib import Path

import structlog

_log = structlog.get_logger(__name__)

_UNSAFE = str.maketrans({"/": "_", "\\": "_", ":": "_", "<": "", ">": ""})


async def save_eml(
    archive_root: Path,
    envelope_id: str,
    source: str,
    ts: datetime,
    raw: bytes,
) -> str:
    """Write raw .eml bytes to the append-only archive.

    Layout: {archive_root}/{source}/{YYYY}/{MM}/{sanitized_id}.eml

    Returns the relative raw_ref path (store this in Envelope.raw_ref).
    Raises FileExistsError if envelope_id is already archived (append-only guarantee).
    """
    safe_id = envelope_id.translate(_UNSAFE)
    rel_path = f"{source}/{ts.year:04d}/{ts.month:02d}/{safe_id}.eml"
    dest = archive_root / rel_path

    dest.parent.mkdir(parents=True, exist_ok=True)

    if dest.exists():
        raise FileExistsError(f"archive: {rel_path} already exists — archive is append-only")

    await asyncio.to_thread(dest.write_bytes, raw)
    _log.info("archive.saved", raw_ref=rel_path, bytes=len(raw))
    return rel_path
