//
// LDASTools frameCPP - Systematic Verify Options Testing Utility
//
// Copyright (C) 2025 California Institute of Technology
//
// This utility systematically tests each Verify class option individually
// to identify which options cause false positive validation failures.
//

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <list>
#include <functional>
#include <unistd.h>  // for _exit()

#include <boost/program_options.hpp>

#include "framecpp/Common/Verify.hh"
#include "framecpp/Common/FrameStream.hh"
#include "framecpp/FrameCPP.hh"
#include "framecpp/FrameCPPDebugManager.hh"

namespace po = boost::program_options;
using FrameCPP::Common::Verify;
using FrameCPP::Common::VerifyException;

//=============================================================================
// Helper: Disable all Verify options
//=============================================================================
void disableAllOptions(Verify& verifier)
{
    verifier.CheckDataValid(false);
    verifier.CheckDataValidAll(false);
    verifier.CheckFast(false);
    verifier.CheckFileChecksum(false);
    verifier.CheckFileChecksumOnly(false);
    verifier.CheckForDuplicateChannelNames(false);
    verifier.CheckFrameChecksum(false);
    verifier.CheckMD5Sum(false);
    verifier.Expandability(false);
    verifier.MustHaveEOFChecksum(false);
    verifier.Strict(false);
    verifier.UseMemoryMappedIO(false);
    verifier.ValidateMetadata(false);
    verifier.CollectAllErrors(false);
}

//=============================================================================
// Helper: Enable single option by name
//=============================================================================
bool enableOption(Verify& verifier, const std::string& option_name)
{
    static const std::map<std::string, std::function<void(Verify&)>> option_map = {
        {"CheckDataValid", [](Verify& v) { v.CheckDataValid(true); }},
        {"CheckDataValidAll", [](Verify& v) { v.CheckDataValidAll(true); }},
        {"CheckFast", [](Verify& v) { v.CheckFast(true); }},
        {"CheckFileChecksum", [](Verify& v) { v.CheckFileChecksum(true); }},
        {"CheckFileChecksumOnly", [](Verify& v) { v.CheckFileChecksumOnly(true); }},
        {"CheckForDuplicateChannelNames", [](Verify& v) { v.CheckForDuplicateChannelNames(true); }},
        {"CheckFrameChecksum", [](Verify& v) { v.CheckFrameChecksum(true); }},
        {"CheckMD5Sum", [](Verify& v) { v.CheckMD5Sum(true); }},
        {"Expandability", [](Verify& v) { v.Expandability(true); }},
        {"MustHaveEOFChecksum", [](Verify& v) { v.MustHaveEOFChecksum(true); }},
        {"Strict", [](Verify& v) { v.Strict(true); }},
        {"UseMemoryMappedIO", [](Verify& v) { v.UseMemoryMappedIO(true); }},
        {"ValidateMetadata", [](Verify& v) { v.ValidateMetadata(true); }},
        {"CollectAllErrors", [](Verify& v) { v.CollectAllErrors(true); }}
    };

    auto it = option_map.find(option_name);
    if (it == option_map.end()) {
        return false;  // Unknown option
    }

    it->second(verifier);
    return true;
}

//=============================================================================
// Main
//=============================================================================
int main(int argc, char* argv[])
{
    try {
        // Initialize frameCPP debug system
        if (!FrameCPP::Utilities::FrameCPPDebugManager::initialize(argc, argv, "test_verify_options")) {
            return 0;
        }

        FrameCPP::Initialize();

        // Command-line options
        po::options_description desc("Systematic Verify Options Testing\n\nAllowed options");
        desc.add_options()
            ("help,h", "Show this help message")
            ("file", po::value<std::vector<std::string>>(), "Frame file(s) to test (required)")
            ("option", po::value<std::string>(),
             "Verify option to enable (if omitted, all options disabled for baseline test)")
            ("list-options", "List all available Verify options")
            ("quiet,q", "Suppress pass/fail messages (exit code only)")
            ("verbose,v", "Show detailed verification information")
        ;

        po::positional_options_description pos;
        pos.add("file", -1);

        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv)
                  .options(desc)
                  .positional(pos)
                  .run(), vm);
        po::notify(vm);

        // Handle --list-options
        if (vm.count("list-options")) {
            std::cout << "Available Verify options:\n";
            std::cout << "  CheckDataValid\n";
            std::cout << "  CheckDataValidAll\n";
            std::cout << "  CheckFast\n";
            std::cout << "  CheckFileChecksum\n";
            std::cout << "  CheckFileChecksumOnly\n";
            std::cout << "  CheckForDuplicateChannelNames\n";
            std::cout << "  CheckFrameChecksum\n";
            std::cout << "  CheckMD5Sum\n";
            std::cout << "  Expandability\n";
            std::cout << "  MustHaveEOFChecksum\n";
            std::cout << "  Strict\n";
            std::cout << "  UseMemoryMappedIO\n";
            std::cout << "  ValidateMetadata\n";
            std::cout << "  CollectAllErrors\n";
            return 0;
        }

        // Handle --help or missing file
        if (vm.count("help") || !vm.count("file")) {
            std::cout << desc << "\n";
            std::cout << "Examples:\n";
            std::cout << "  Phase 0 (baseline): test_verify_options file.gwf\n";
            std::cout << "  Phase 1 (individual): test_verify_options --option CheckFileChecksum file.gwf\n";
            std::cout << "  Multiple files: test_verify_options --option CheckFileChecksum file1.gwf file2.gwf\n";
            return vm.count("help") ? 0 : 1;
        }

        std::vector<std::string> files = vm["file"].as<std::vector<std::string>>();
        std::string option_name = vm.count("option") ? vm["option"].as<std::string>() : "";
        bool quiet = vm.count("quiet");
        bool verbose = vm.count("verbose");

        // Configure Verify object
        Verify verifier;
        disableAllOptions(verifier);  // Start with all options disabled

        // Enable specific option if requested
        if (!option_name.empty()) {
            if (!enableOption(verifier, option_name)) {
                std::cerr << "ERROR: Unknown option '" << option_name << "'\n";
                std::cerr << "Use --list-options to see available options\n";
                return 1;
            }
            if (!quiet) {
                std::cout << "Testing with option: " << option_name << "\n";
            }
        } else {
            if (!quiet) {
                std::cout << "Testing with ALL options disabled (baseline)\n";
            }
        }

        // Test each file
        int exit_code = 0;
        for (const auto& filename : files) {
            try {
                if (verbose) {
                    std::cout << "Verifying: " << filename << "\n";
                }

                verifier(filename);

                if (!quiet) {
                    std::cout << filename << ": PASS\n";
                }
            }
            catch (const VerifyException& e) {
                if (!quiet) {
                    std::cout << filename << ": FAIL - " << e.what() << "\n";
                }
                exit_code = e.ErrorCode();
            }
            catch (const std::exception& e) {
                if (!quiet) {
                    std::cout << filename << ": ERROR - " << e.what() << "\n";
                }
                exit_code = 99;
            }
        }

        // Use _exit() to bypass static destructor cleanup which has mutex bugs
        _exit(exit_code);
    }
    catch (const std::exception& e) {
        std::cerr << "FATAL ERROR: " << e.what() << "\n";
        _exit(99);
    }
}
