#pragma once
// provenance — the REAPER-free core behind re-capture-from-source. The shell
// gathers raw inputs from REAPER (source media-file names, FX-chain identity,
// capture range/scope/tail) and hands plain strings/values here. Owns:
//   * CaptureRecipe     — the recorded request + source FX-chain identity, so
//                         re-capture can re-run the same request and detect drift.
//   * fingerprint codec — encodes/decodes a recipe into the single
//                         Provenance.fxChainSnapshot string (no schema change).
//   * fxChainIdentity   — folds FX-chain rows into one drift-detection string.
//   * detectParent      — pure parent-detection: resolved file path only, no
//                         fuzzy match, no false parentage.
//
// A THIN reproducibility fingerprint (drift-detect + re-run) — NOT a serialized FX
// chunk to restore; nothing here stores a restorable chain.

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

namespace reasampler::model {

// Capture scope, mirrored from render_settings' CaptureScope but kept independent
// here so the pure provenance module does not pull the whole render_settings graph
// in. The shell maps its CaptureScope onto this two-value enum. Item = item/take FX
// only; Track = item FX + the track's own FX (CLAUDE.md §Capture FX scope).
enum class ProvenanceScope {
    Item,
    Track,
};

// One FX-chain entry as the shell reads it from REAPER. For Track scope the shell
// uses TrackFX_GetFXName/GetFXGUID/GetEnabled; for Item scope it uses the TakeFX_*
// equivalents over the active take's FX chain. Plain data — the shell fills it, the
// pure fold turns the vector into the identity string.
struct FxIdentityEntry {
    std::string name;     // TrackFX_GetFXName / TakeFX_GetFXName
    std::string guid;     // TrackFX_GetFXGUID / TakeFX_GetFXGUID -> guidToString
    bool        enabled;  // TrackFX_GetEnabled / TakeFX_GetEnabled
};

// The recorded capture recipe + source FX-chain identity — the thin fingerprint.
// Re-capture replays the request fields verbatim against the source's CURRENT
// state; fxChainIdentity is compared post-hoc to report drift. Ordinary equality
// (via ==) is a full recipe match; fxChainIdentity difference alone is "the source
// drifted but the recipe is the same" (the re-run still succeeds, the user is told).
struct CaptureRecipe {
    ProvenanceScope scope = ProvenanceScope::Track;
    int             sourceMode = 0;   // reasampler::SourceMode as int (bank_model)

    double startSeconds = 0.0;        // exact bounds — no rounding (invariant)
    double endSeconds   = 0.0;

    int    tailMode     = 0;          // reasampler::TailMode as int (render_settings)
    double tailMs       = 0.0;

    int    sampleRate   = 0;          // 0 = follow project rate
    int    channelCount = 2;

    // Canonical GUID strings of the source track(s) the capture came from
    // (guidString form). Re-capture resolves these back to live tracks.
    std::vector<std::string> trackGuids;

    // The source FX-chain identity at capture time — the drift component. A folded
    // string (fxChainIdentity) of the in-scope FX rows. Not a restorable chunk.
    std::string fxChainIdentity;

    bool operator==(const CaptureRecipe& o) const;
    bool operator!=(const CaptureRecipe& o) const { return !(*this == o); }
};

// Folds the shell-gathered FX rows into ONE identity string. Order-sensitive
// (chain order is part of identity), delimited so a name containing the delimiter
// cannot forge a different chain (the fields are length-prefixed). Empty vector ->
// empty string (a no-FX source has an empty, stable identity). Pure + deterministic.
std::string fxChainIdentity(const std::vector<FxIdentityEntry>& entries);

// Combines several per-track FX-chain identity strings (one per source track, in
// track order) into ONE identity, length-prefixing each so two different per-track
// partitions can never collide by concatenation (e.g. {"X",""} != {"","X"}). Used
// for a multi-track Track-scope capture. A single-track capture combines to a
// stable, unambiguous wrapping of its one identity. Pure + deterministic.
std::string combineChainIdentities(const std::vector<std::string>& perTrack);

// Encodes a CaptureRecipe into the single string stored in
// Provenance.fxChainSnapshot. Self-describing, versioned, and escape-safe so it
// round-trips losslessly through the Sample JSON (which treats the whole thing as
// one opaque string value). buildFingerprint(x) then parseFingerprint(...) == x.
std::string buildFingerprint(const CaptureRecipe& recipe);

// Parses a fingerprint produced by buildFingerprint. Returns nullopt on any
// malformed / unrecognized-version input (never throws, never UB) so a legacy or
// corrupt provenance string degrades to "no recipe" gracefully rather than
// mis-driving a re-capture.
std::optional<CaptureRecipe> parseFingerprint(const std::string& fingerprint);

// -- Parent detection: identity by resolved file path only --------------------

// One bank sample as the detector sees it: its stable id and the ABSOLUTE,
// normalized path its file resolves to (the shell resolves relativePath against
// the current project dir via resolveBankFile + normalizeSlashes before handing
// it here). Plain data so the decision is pure and testable.
struct BankFileRef {
    std::string sampleId;
    std::string absolutePath;  // normalized (forward-slash, no trailing slash)
};

// Decides whether a capture derives from a bank sample. No false parentage: a
// capture derives from a bank sample iff EVERY source item whose media file could
// be resolved points at the SAME bank sample's file (exact normalized absolute
// path). Files not in the bank, or matching MORE THAN ONE distinct bank sample
// (ambiguous), yield no parent. An empty source-file set yields no parent.
//
//   sourceItemFiles : normalized absolute paths of the capture's source items'
//                     take media files, gathered by the shell. A file that could
//                     not be resolved is simply omitted — never an empty string.
//   bankFiles       : the active book's samples as BankFileRefs (path -> id).
//
// Returns the parent sample id, or nullopt otherwise. Both sides are normalized
// identically via normalizeSlashes (lowercased on Windows) so a slash/case
// difference never spuriously matches or misses.
std::optional<std::string> detectParent(
    const std::vector<std::string>& sourceItemFiles,
    const std::vector<BankFileRef>& bankFiles);

}  // namespace reasampler::model
