#pragma once
// package_manifest — the RSBK manifest model and its JSON codec. Per-sample
// shape is NOT owned here: each entry nests a one-sample BankModel blob emitted
// by bank_model's own writer (the bank_book_json precedent), so a future Sample
// field reaches packages for free. Pure: no filesystem, no host types.

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

#include "core/model/bank_model.h"
#include "core/model/slot_map.h"

namespace reasampler::package {

// One payload's transport record. `byteHash` is capture::hashBytes over the
// payload's raw bytes — the whole-file digest, deliberately NOT hashWavContent
// (which skips chunks and so cannot answer "did these bytes survive the trip").
// FNV-1a: a corruption detector, not a cryptographic checksum. The codec only
// carries the digest; hashing happens where the payload is streamed (shell).
struct PackageEntry {
    std::string fileName;            // bare name inside the package (isValidEntryName)
    std::uint64_t byteLength = 0;
    std::string byteHash;
    model::Sample sample;

    bool operator==(const PackageEntry& o) const;
};

// Everything the manifest carries besides the payloads: informational envelope
// (source bank name, export moment), the entries, and the bank's display
// positions (a bank's arrangement is part of what the user built).
struct PackageManifest {
    std::string bankDisplayName;
    std::int64_t exportTimestamp = 0;  // unix epoch seconds
    std::vector<PackageEntry> entries;
    model::SlotMap slots;

    bool operator==(const PackageManifest& o) const;
};

// Emits the manifest JSON. nullopt when the manifest cannot be represented: an
// invalid or duplicate entry name (duplicate by sameEntryName, not string
// equality), a nested relativePath isValidNestedSamplePath refuses, a
// zero-length entry (see this directory's CLAUDE.md — the shell's payload-append
// seam cannot round-trip one), or a sample record BankModel itself would reject
// (empty id, absolute path) — refusing on encode so an undecodable package is
// never written.
std::optional<std::string> serializeManifest(const PackageManifest& m);

// Parses manifest JSON (nullopt on malformed input, never UB). Unknown keys are
// skipped at every level, so an additive newer manifest still parses; a repeated
// KNOWN root key is rejected rather than last-wins. Rejects what encode rejects
// except the zero-length entry — names and nested paths are validated on BOTH
// directions because a package can arrive from anywhere — plus a missing
// per-entry field or a negative length.
std::optional<PackageManifest> deserializeManifest(const std::string& json);

}  // namespace reasampler::package
