"""Artifact materialization boundaries used by ARR delivery validation."""

from __future__ import annotations

import os
import shutil
from pathlib import Path
from typing import Protocol

from arr_ingestion.contracts import IngestionError


class ArtifactStore(Protocol):
    def materialize(self, object_key: str, destination: Path, max_bytes: int) -> None:
        """Copy one immutable object to a new private local file."""


class LocalObjectStore:
    """Controlled local implementation for tests and offline vertical slices."""

    def __init__(self, root: Path) -> None:
        self._root = root.resolve()
        if not self._root.is_dir():
            raise ValueError("object store root must be an existing directory")

    def materialize(self, object_key: str, destination: Path, max_bytes: int) -> None:
        source = self._root.joinpath(*object_key.split("/"))
        try:
            resolved = source.resolve(strict=True)
            resolved.relative_to(self._root)
        except (FileNotFoundError, OSError, ValueError):
            raise IngestionError("ARTIFACT_NOT_FOUND", "delivery artifact is unavailable") from None
        if source.is_symlink() or not resolved.is_file():
            raise IngestionError("ARTIFACT_NOT_FOUND", "delivery artifact is unavailable")
        destination.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
        descriptor = os.open(destination, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
        copied = 0
        try:
            with resolved.open("rb") as source_handle, os.fdopen(descriptor, "wb") as target:
                descriptor = -1
                while True:
                    chunk = source_handle.read(1024 * 1024)
                    if not chunk:
                        break
                    copied += len(chunk)
                    if copied > max_bytes:
                        raise IngestionError(
                            "ARTIFACT_TOO_LARGE", "delivery artifact exceeds its size limit"
                        )
                    target.write(chunk)
                target.flush()
                os.fsync(target.fileno())
        except Exception:
            destination.unlink(missing_ok=True)
            raise
        finally:
            if descriptor >= 0:
                os.close(descriptor)


class DirectoryArtifactPublisher:
    """Small helper for synthetic/local flows; production uses the OSS adapter."""

    def __init__(self, root: Path) -> None:
        self._root = root.resolve()
        self._root.mkdir(parents=True, exist_ok=True, mode=0o700)

    def put_file(self, source: Path, object_key: str) -> Path:
        destination = self._root.joinpath(*object_key.split("/"))
        try:
            destination.resolve().relative_to(self._root)
        except ValueError:
            raise IngestionError("ARTIFACT_REFERENCE_INVALID", "object key is invalid") from None
        destination.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
        if destination.exists():
            raise IngestionError("ARTIFACT_CONFLICT", "object key already exists")
        descriptor = os.open(destination, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
        try:
            with source.open("rb") as source_handle, os.fdopen(descriptor, "wb") as target:
                descriptor = -1
                shutil.copyfileobj(source_handle, target, length=1024 * 1024)
                target.flush()
                os.fsync(target.fileno())
        except Exception:
            destination.unlink(missing_ok=True)
            raise
        finally:
            if descriptor >= 0:
                os.close(descriptor)
        return destination
