//
// LDASTools frameCPP - Frame File Information Utility (Refactored)
//
// Copyright (C) 2025 California Institute of Technology
//
// LDASTools frameCPP is free software; you may redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 (GPLv2) of the
// License or at your discretion, any later version.
//
// LDASTools frameCPP is distributed in the hope that it will be useful, but
// without any warranty or even the implied warranty of merchantability
// or fitness for a particular purpose. See the GNU General Public
// License (GPLv2) for more details.

#include <iostream>
#include <vector>
#include <string>
#include <filesystem>
#include <memory>

#include <boost/program_options.hpp>

#include "ldastoolsal/LoggerManager.hh"
#include "framecpp/Common/FrameCPPServices.hh"
#include "framecpp/FrameFileInfo.hh"
#include "framecpp/FrameCPPDebugManager.hh"

namespace po = boost::program_options;
using FrameCPP::OOInterface::DisplayConfig;
using FrameCPP::OOInterface::FrameField;
using FrameCPP::OOInterface::FrameFileAnalyzer;
using FrameCPP::OOInterface::FrameInfoFormatter;

/// @brief Parse command-line arguments and return configuration
/// @param argc Command-line argument count
/// @param argv Command-line argument vector
/// @return Pair of display configuration and file list
std::pair< DisplayConfig, std::vector< std::filesystem::path > >
parseArguments( int argc, char* argv[] )
{
    DisplayConfig                        config;
    std::vector< std::filesystem::path > files;

    // Program options
    po::options_description general( "General options" );
    general.add_options( )( "help,h", "Show this help message" )(
        "verbose,v", "Verbose output (human-readable)" )(
        "terse,t", "Terse output (machine-parsable)" )(
        "show-filename,f", "Include filename in terse output" );

    po::options_description field_opts( "Field selection options" );
    field_opts.add_options( )( "originator", "Display originator field only" )(
        "frame-spec-version", "Display frame specification version only" )(
        "library-revision", "Display library revision only" )(
        "library-name", "Display library name (v8+ only)" )(
        "checksum-scheme", "Display checksum scheme (v8+ only)" )(
        "endianness", "Display byte order information" )(
        "data-sizes", "Display data type sizes" );

    po::options_description debug_opts( "Debug options" );
    debug_opts.add_options( )(
        "debug-level", po::value< int >( ), "Set global debug level" )(
        "debug-channel",
        po::value< std::vector< std::string > >( )->multitoken( ),
        "Set debug level for specific channel(s)" )(
        "debug-level-channel",
        po::value< std::vector< std::string > >( )->multitoken( ),
        "Set channel and level (channel:level)" )( "help-debug",
                                                   "Show debug help" );

    po::options_description hidden( "Hidden options" );
    hidden.add_options( )( "input-files",
                           po::value< std::vector< std::string > >( ),
                           "Input files" );

    po::options_description all_options;
    all_options.add( general )
        .add( field_opts )
        .add( debug_opts )
        .add( hidden );

    po::options_description visible_options(
        "framecpp_info - Display FrHeader information from .gwf "
        "files\n\nUsage: framecpp_info [options] <file1> [file2] "
        "...\n\nOptions" );
    visible_options.add( general ).add( field_opts ).add( debug_opts );

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

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

    // Help message
    if ( vm.count( "help" ) )
    {
        std::cout << visible_options << std::endl;
        exit( 0 );
    }

    // Check for input files
    if ( !vm.count( "input-files" ) )
    {
        std::cerr << "Error: No input files specified" << std::endl;
        std::cerr << "Usage: framecpp_info [options] <file1> [file2] ..."
                  << std::endl;
        exit( 1 );
    }

    // Determine display mode
    if ( vm.count( "terse" ) )
    {
        config.mode = DisplayConfig::Mode::Terse;
    }

    // Check if filename should be shown in terse mode
    config.show_filename = vm.count( "show-filename" ) > 0;

    // Determine which fields to display using modern C++17 approach
    config.fields.clear( ); // Clear default "All" setting

    std::vector< std::pair< std::string, FrameField > > field_mappings = {
        { "originator", FrameField::Originator },
        { "frame-spec-version", FrameField::FrameSpecVersion },
        { "library-revision", FrameField::LibraryRevision },
        { "library-name", FrameField::LibraryName },
        { "checksum-scheme", FrameField::ChecksumScheme },
        { "endianness", FrameField::Endianness },
        { "data-sizes", FrameField::DataSizes }
    };

    for ( const auto& [ option_name, field ] : field_mappings )
    {
        if ( vm.count( option_name ) )
        {
            config.fields.insert( field );
        }
    }

    // If no specific fields requested, show all
    if ( config.fields.empty( ) )
    {
        config.fields.insert( FrameField::All );
    }

    // Convert input files to filesystem paths
    auto file_strings = vm[ "input-files" ].as< std::vector< std::string > >( );
    files.reserve( file_strings.size( ) );
    for ( const auto& file_str : file_strings )
    {
        files.emplace_back( file_str );
    }

    return { config, files };
}

int
main( int argc, char* argv[] )
{
    try
    {
        // Parse command-line arguments using modern structured bindings
        auto [ config, files ] = parseArguments( argc, argv );

        // Initialize services with dependency injection
        auto logger_mgr = std::make_unique< LDASTools::AL::LoggerManager >( );
        FrameCPP::Common::FrameCPPServices services( *logger_mgr );

        // Initialize debug manager
        if ( !FrameCPP::Utilities::FrameCPPDebugManager::
                 initialize_with_services(
                     argc, argv, services, "framecpp_info" ) )
        {
            logger_mgr->shutdown( );
            return 0; // Help was shown
        }

        // Create analyzer and formatter using shared classes
        FrameFileAnalyzer  analyzer( services );
        FrameInfoFormatter formatter( config );

        // Process files using modern batch processing
        auto results = analyzer.analyzeFiles( files );
        formatter.formatBatch( results );

        // Clean shutdown
        logger_mgr->shutdown( );
        return 0;
    }
    catch ( const std::exception& e )
    {
        std::cerr << "Error: " << e.what( ) << std::endl;
        return 1;
    }
}
