from __future__ import annotations

import hashlib
import mimetypes
import shutil
import uuid
from dataclasses import dataclass
from pathlib import Path
from typing import Optional

from config.paths import DATA_ROOT


_ATTACHMENTS_ROOT = DATA_ROOT / "receipts" / "artifacts"
_BUFFER_SIZE = 1 << 16


@dataclass(frozen=True)
class AttachmentInfo:
    """Resolved attachment metadata stored alongside receipts."""

    path: Path
    rel_path: str
    digest: str
    kind: Optional[str] = None


def _compute_digest(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as handle:
        while True:
            chunk = handle.read(_BUFFER_SIZE)
            if not chunk:
                break
            digest.update(chunk)
    return f"sha256:{digest.hexdigest()}"


def _ensure_destination(category: Optional[str] = None) -> Path:
    base = _ATTACHMENTS_ROOT
    if category:
        base = base / category
    base.mkdir(parents=True, exist_ok=True)
    return base


def register_attachment(
    source: Path,
    *,
    category: Optional[str] = None,
    name: Optional[str] = None,
) -> AttachmentInfo:
    """
    Copy an existing artifact into the managed attachments directory.

    Returns metadata including the relative path (from DATA_ROOT) and digest.
    """
    source_path = Path(source)
    if not source_path.exists():
        raise FileNotFoundError(f"attachment source does not exist: {source_path}")

    destination_dir = _ensure_destination(category)
    token = uuid.uuid4().hex[:12]
    suffix = name or source_path.name
    destination = destination_dir / f"{token}_{suffix}"
    shutil.copy2(source_path, destination)

    digest = _compute_digest(destination)
    rel_path = destination.relative_to(DATA_ROOT)
    mime, _ = mimetypes.guess_type(destination.name)
    return AttachmentInfo(path=destination, rel_path=str(rel_path), digest=digest, kind=mime)


def write_bytes_attachment(
    payload: bytes,
    filename: str,
    *,
    category: Optional[str] = None,
) -> AttachmentInfo:
    """
    Persist bytes as a managed attachment and return the metadata.
    """
    destination_dir = _ensure_destination(category)
    token = uuid.uuid4().hex[:12]
    destination = destination_dir / f"{token}_{filename}"
    with destination.open("wb") as handle:
        handle.write(payload)

    digest = _compute_digest(destination)
    rel_path = destination.relative_to(DATA_ROOT)
    mime, _ = mimetypes.guess_type(destination.name)
    return AttachmentInfo(path=destination, rel_path=str(rel_path), digest=digest, kind=mime)


__all__ = ["AttachmentInfo", "register_attachment", "write_bytes_attachment"]
