// Parity harness: run the C++ XGBSceneBoundary on a dump's frames/rgb_hist and
// print the boundary timestamps, so they can be diffed against the Python
// knee_boundaries (scripts/scene_detector). Feature parity is the whole risk of
// the C++ port; this proves it before wiring into the pipeline.
//
//   xgb_boundary_parity <dump.h5> <model.json>
//
// Prints: "<n> boundaries: t0 t1 t2 ..."

#include "inference/xgb_scene_boundary.hpp"
#include "inference/audio_logpsd.hpp"
#include <H5Cpp.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

int main(int argc, char** argv) {
    if (argc < 3) { std::cerr << "usage: xgb_boundary_parity <dump.h5> <model.json>\n"; return 1; }
    H5::H5File f(argv[1], H5F_ACC_RDONLY);

    auto read2d = [&](const char* name, std::vector<std::vector<float>>& out, int cols) {
        H5::DataSet ds = f.openDataSet(name);
        H5::DataSpace sp = ds.getSpace();
        hsize_t dims[2]; sp.getSimpleExtentDims(dims);
        std::vector<float> flat(dims[0]*dims[1]);
        ds.read(flat.data(), H5::PredType::NATIVE_FLOAT);
        out.assign(dims[0], std::vector<float>(cols));
        for (hsize_t i = 0; i < dims[0]; ++i)
            for (int j = 0; j < cols; ++j) out[i][j] = flat[i*dims[1]+j];
    };
    std::vector<std::vector<float>> hist;
    read2d("frames/rgb_hist", hist, XGBSceneBoundary::kHistBins*3);

    H5::DataSet tsd = f.openDataSet("frames/timestamp_sec");
    hsize_t td[1]; tsd.getSpace().getSimpleExtentDims(td);
    std::vector<double> ts(td[0]);
    tsd.read(ts.data(), H5::PredType::NATIVE_DOUBLE);

    // parity debug: print video features for row 100 (compare to Python)
    if (argc > 3 && std::string(argv[3]) == "--row100") {
        auto base = XGBSceneBoundary::debug_base(hist, {});
        std::cout << "row100:";
        for (int j = 0; j < 17; ++j) std::cout << " " << base[100][j];
        std::cout << "\n";
        return 0;
    }

    // --feat <cpp_features.h5>: predict directly on the dumped C++ feature matrix
    // (same bytes Python reads) — a clean parity check with no live-decode variance.
    if (argc > 4 && std::string(argv[3]) == "--feat") {
        H5::H5File ff(argv[4], H5F_ACC_RDONLY);
        H5::DataSet fd = ff.openDataSet("features");
        hsize_t fdm[2]; fd.getSpace().getSimpleExtentDims(fdm);
        std::vector<float> X(fdm[0]*fdm[1]);
        fd.read(X.data(), H5::PredType::NATIVE_FLOAT);
        XGBSceneBoundary det(argv[2]);
        auto pdbg = det.debug_predict(X, int(fdm[0]), int(fdm[1]));
        auto pk = XGBSceneBoundary::debug_find_peaks(pdbg, 5);
        std::cerr << "[parity] C++ raw peaks=" << pk.size() << "\n";
        auto b = det.boundaries_from_features(X, int(fdm[0]), int(fdm[1]), ts);
        std::cout << b.size() << " boundaries:";
        for (double t : b) std::cout << " " << int(t);
        std::cout << "\n";
        return 0;
    }

    // Optional movie path (argv[4]): decode audio → per-second log-PSD.
    std::vector<std::vector<float>> audio;
    if (argc > 4) {
        audio = AudioLogPSD::extract(argv[4]);
        std::cerr << "[parity] audio rows=" << audio.size()
                  << " (hist rows=" << hist.size() << ")\n";
    }

    XGBSceneBoundary det(argv[2]);
    auto b = det.boundaries(hist, ts, audio);
    std::cout << b.size() << " boundaries:";
    for (double t : b) std::cout << " " << int(t);
    std::cout << "\n";
    return 0;
}
