"""Chunker API used by gnos core and external plugins.

Chunkers operate on raw bytes and may attach method-defined per-chunk metadata.
gnos computes chunk hashes and stores the metadata alongside `hash` in the
manifest's `chunks` entries.
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any, Dict, List


ChunkEntry = Dict[str, Any]


@dataclass(frozen=True)
class ChunkerContext:
    """Split-time context for chunkers.

    Chunkers MUST NOT depend on this context for reconstruction, since attestation
    uses only the manifest + chunk bytes. If a chunker uses context in a way that
    affects the produced chunks, it MUST be captured in the returned effective
    params and/or in the per-chunk manifest entry metadata.

    All fields are optional. Future gnos flows may ingest bytes without a
    filesystem path (e.g. stdin/remote bytes), in which case `path`/`format` may
    be missing.
    """

    path: str | None = None
    format: str | None = None


@dataclass(frozen=True)
class SplitChunk:
    """Chunk emitted by `Chunker.split()`.

    - `data`: bytes that will be stored in the object store (by hash).
    - `entry`: method-defined metadata to be written into the manifest next to
      `hash` (which is computed by gnos).

    The chunker MUST NOT include `hash` in `entry`.
    """

    data: bytes
    entry: ChunkEntry = field(default_factory=dict)


@dataclass(frozen=True)
class SplitResult:
    """Result of `Chunker.split()`.

    - `chunks`: ordered list of emitted chunks
    - `params`: effective parameters that were used (will be stored into the
      manifest's `chunking.params`)
    """

    chunks: List[SplitChunk]
    params: Dict[str, Any]


@dataclass(frozen=True)
class ManifestChunk:
    """Chunk input provided to `Chunker.reconstruct()`.

    - `data`: chunk bytes loaded from the object store by `entry["hash"]`.
    - `entry`: the exact chunk entry object from the manifest (including `hash`)
      and any method-defined metadata.
    """

    data: bytes
    entry: ChunkEntry


class Chunker(ABC):
    """Chunker interface for core and plugin implementations."""

    name: str

    def __repr__(self) -> str:  # pragma: no cover - trivial
        return f"{self.__class__.__name__}(name={self.name!r})"

    def describe(self) -> str:
        """Return a human-readable chunker identifier."""
        return self.name

    @abstractmethod
    def split(self, data: bytes, params: Dict[str, Any], ctx: ChunkerContext) -> SplitResult:
        """Split document bytes into chunks (with optional per-chunk metadata).

        Returns the effective params that MUST be stored into the manifest to
        describe this split deterministically.
        """

    @abstractmethod
    def reconstruct(self, chunks: List[ManifestChunk], params: Dict[str, Any]) -> bytes:
        """Deterministically reconstruct original bytes from chunks + metadata."""
