"""Opaque OSS exchange handles used between the trusted Agent runtime and ARR."""

from __future__ import annotations

import os
from dataclasses import dataclass
from pathlib import Path
from typing import Mapping, Optional

from arr_ingestion.contracts import OPAQUE_ID_RE
from arr_storage.contracts import BackendObject, valid_object_key
from arr_storage.remote import CloudClientError, CloudClientPort


def _prefix(value: str) -> str:
    normalized = value.strip().strip("/")
    probe = normalized + "/file_handle"
    if not normalized or not valid_object_key(probe):
        raise ValueError("Agent output exchange prefix is invalid")
    return normalized


@dataclass(frozen=True)
class OutputExchangeConfig:
    prefix: str = "arr-agent-outputs"

    def __post_init__(self) -> None:
        object.__setattr__(self, "prefix", _prefix(self.prefix))

    @classmethod
    def from_environment(
        cls,
        environment: Optional[Mapping[str, str]] = None,
    ) -> "OutputExchangeConfig":
        values = environment if environment is not None else os.environ
        return cls(str(values.get("ARR_AGENT_OUTPUT_PREFIX", "arr-agent-outputs")))

    def object_key(self, file_handle: str) -> str:
        if not OPAQUE_ID_RE.fullmatch(file_handle):
            raise ValueError("Agent output file handle is invalid")
        return f"{self.prefix}/{file_handle}"


class OutputExchangePublisher:
    """Runtime-side idempotent publisher for one opaque output handle."""

    def __init__(self, client: CloudClientPort, config: OutputExchangeConfig) -> None:
        self._client = client
        self._config = config

    def publish(
        self,
        *,
        file_handle: str,
        source: Path,
        mime_type: str,
        metadata: Mapping[str, str],
    ) -> BackendObject:
        object_key = self._config.object_key(file_handle)
        try:
            return self._client.upload_file_if_absent(
                object_key,
                str(source),
                mime_type,
                metadata,
            )
        except CloudClientError as error:
            if error.kind != "conflict":
                raise
            existing = self._client.stat_object(object_key)
            if (
                existing.byte_size != int(metadata.get("arr-byte-size", "-1"))
                or existing.metadata.get("arr-sha256") != metadata.get("arr-sha256")
                or existing.metadata.get("arr-mime-type") != mime_type
            ):
                raise CloudClientError("conflict") from None
            return existing
