#pragma once
// shell/package/import_landing — the import's filesystem half and its index half,
// both REAPER-free so the all-or-nothing, integrity and birth-record properties are
// assertable without a DAW. The verb that drives them against a live session is
// import_bank; the REAPER-facing reporting is shell/actions/package_import_action.

#include <functional>
#include <string>

#include "core/model/bank_book.h"
#include "core/model/bank_model.h"
#include "core/package/import_plan.h"
#include "core/package/package_format.h"
#include "shell/package/package_rollback.h"

namespace reasampler {

// How a landing ended. Every value but Landed means NOTHING is on disk and NO index was
// touched. NoProject/Unreadable/Malformed/TooNew refuse before a byte is written.
// IntegrityFailed also refuses before any write — the full-package digest verification
// runs to completion first (landPackage) — so it needs no rollback either. WriteFailed
// is the only outcome that actually wrote and then rolled back. IndexRejected is never
// returned by landPackage/this struct — it is import_bank's own outcome, minted after a
// successful landing when the book itself refuses the create.
enum class ImportOutcome {
    Landed,
    NoProject,        // unsaved project: there is no bank folder to land into
    Unreadable,       // the package file could not be opened
    Malformed,        // not a well-formed RSBK: corrupt, truncated, or trailing garbage
    TooNew,           // minReaderVersion above this build's ladder
    IntegrityFailed,  // an entry's payload did not match its recorded digest; pre-write refusal
    WriteFailed,      // a write failed partway; the landed files were rolled back
    IndexRejected,    // never set here — see the comment above; import_bank's outcome only
};

struct ImportLanding {
    ImportOutcome outcome = ImportOutcome::Unreadable;
    // Meaningful from the moment the header parsed — a TooNew refusal names the
    // writer's build, which is the only part of that message a user can act on.
    package::PackageHeader header;
    package::ImportPlan    plan;
    std::string            failedEntryName;  // IntegrityFailed / WriteFailed
    RollbackResult         rollback;         // WriteFailed only — IntegrityFailed leaves it default
};

// Streams `packageAbsPath` into the project's bank folder: decode, plan, verify EVERY
// payload's digest, then land. Verification runs to completion before the first write,
// so a damaged package costs no rollback at all. Mutates no index and holds at most
// one payload at a time. `journal` is left armed on success — the caller applies the
// plan to the book and only then disarms it.
ImportLanding landPackage(const std::string& packageAbsPath,
                          const std::string& projectDir,
                          const BankBook& destination,
                          const std::string& uniqueTag,
                          LandedFileJournal& journal);

// Called for every landed file, in the same straight-line block as its bank add —
// core/tracking/CLAUDE.md's no-silent-gaps invariant, kept structural by passing the
// writer in rather than letting a caller add first and record later.
using RecordBirth = std::function<void(const model::Sample&)>;

// Adds the plan's landed entries to a NEW bank under `bankId`. False (no mutation)
// only if the book refuses the create — the name was minted free against this same
// book, so that means the book changed underneath the plan.
bool applyImportedBank(BankBook& book, const std::string& bankId,
                       const package::ImportPlan& plan, const RecordBirth& recordBirth);

}  // namespace reasampler
