#pragma once
// bank_package — RSBK framing and layout arithmetic: header encode, prefix
// decode, and the ordered {name, offset, length} entry layout. See this
// directory's CLAUDE.md for the framing-only invariant. Pure: no filesystem.

#include <cstdint>
#include <optional>
#include <string>
#include <vector>

#include "core/package/package_format.h"
#include "core/package/package_manifest.h"

namespace reasampler::package {

// Where one payload sits in the finished package file (absolute byte offset).
struct PackageEntrySpan {
    std::string name;
    std::uint64_t offset = 0;
    std::uint64_t length = 0;

    bool operator==(const PackageEntrySpan& o) const {
        return name == o.name && offset == o.offset && length == o.length;
    }
};

// The write side's product: the prefix bytes (magic through manifest, written
// verbatim as the file's head), the layout to stream each payload at, and the
// finished file's exact size — what the shell verifies after the last append.
struct EncodedPackage {
    std::vector<std::uint8_t> prefix;
    std::vector<PackageEntrySpan> layout;
    std::uint64_t totalSize = 0;
};

// Encodes the package prefix for `m`, stamping this build's version pair and
// version::stampVersion() as the writer semver. nullopt when the manifest
// cannot be represented (serializeManifest's rejections) — refused on encode so
// an undecodable package is never written.
std::optional<EncodedPackage> encodePackage(const PackageManifest& m);

// The read side's product. header is meaningful for Readable and TooNew (a
// refusal must still name the writer), and on any Malformed reached after the
// header parsed (a corrupt manifest at this build's own version carries the
// real pair, not the 0/0 unparsed default); manifest, layout, and prefixSize
// only for Readable — TooNew produces NO manifest, so a refused decode cannot
// half-succeed.
struct DecodedPackage {
    PackageReadability status = PackageReadability::Malformed;
    PackageHeader header;
    PackageManifest manifest;
    std::vector<PackageEntrySpan> layout;
    std::uint64_t prefixSize = 0;
};

// Decodes a package's leading bytes. `totalFileSize` is the on-disk size the
// caller observed: decode proves prefix + payload lengths equal it exactly, so
// a truncated or garbage-extended file is Malformed even though the payloads
// themselves are never read here. `prefix` may be the whole file or any head of
// it that requiredPrefixSize accepted. Error signaled, never UB.
DecodedPackage decodePackage(const std::vector<std::uint8_t>& prefix,
                             std::uint64_t totalFileSize);

// How many leading bytes decodePackage needs. May grow as bytes arrive: with
// fewer than the returned count on hand, read to that count and ask again.
// For a TooNew package it stops at the frozen region (through the writer
// semver) — field positions beyond it belong to the newer format and are not
// trusted. nullopt: these bytes can never frame a package (bad magic,
// incoherent versions, an over-cap length field) — stop reading.
std::optional<std::uint64_t> requiredPrefixSize(const std::vector<std::uint8_t>& bytes);

}  // namespace reasampler::package
