#include <memory>

#include "SimpleLogger.hh"

// Initialize static member
bool SimpleLogger::shutdown_initiated_ = false;

#include <boost/log/utility/setup/common_attributes.hpp>

#include "ldastoolsal/gpstime.hh"

BOOST_LOG_ATTRIBUTE_KEYWORD( severity,
                             "Severity",
                             SimpleLogger::custom_severity_level )

/// @brief Null sink definition.
class null_sink : public boost::log::sinks::basic_sink_backend<
                      boost::log::sinks::combine_requirements<
                          boost::log::sinks::synchronized_feeding >::type >
{
public:
    /// @brief Consume method to handle log records.
    /// @param rec Logging record view
    void
    consume( boost::log::record_view const& rec )
    {
    }
};

std::ostream&
operator<<( std::ostream& os, const SimpleLogger::custom_severity_level& level )
{
    if ( level.debug_level >= 0 )
    {
        os << "Debug(" << level.debug_level << ")";
    }
    else
    {
        switch ( level.severity )
        {
        case SimpleLogger::severity_level::ok:
            os << "OK";
            break;
        case SimpleLogger::severity_level::note:
            os << "Note";
            break;
        case SimpleLogger::severity_level::warning:
            os << "Warning";
            break;
        case SimpleLogger::severity_level::error_non_fatal:
            os << "Error (Non-Fatal)";
            break;
        case SimpleLogger::severity_level::error:
            os << "Error";
            break;
        case SimpleLogger::severity_level::email:
            os << "Email";
            break;
        case SimpleLogger::severity_level::phone:
            os << "Phone";
            break;
        default:
            os << "Unknown";
            break;
        }
    }
    return os;
}

//-----------------------------------------------------------------------
//
//-----------------------------------------------------------------------
class GifDB
{
public:
    struct gif_info
    {
        gif_info( ) = delete;
        ~gif_info( ) = default;
        gif_info& operator=( const gif_info& ) = delete;

        enum alt_type
        {
            ALT_TIME,
            ALT_EMAIL
        };

        const char*    s_name;
        const char*    s_alt_name;
        const char*    s_alt_desc;
        const char*    s_description;
        const int      s_width;
        const int      s_height;
        const alt_type s_alt_info;
    };

    static GifDB&
    getInstance( )
    {
        std::call_once( initFlag, &GifDB::init );
        return *instance;
    };

    GifDB( const GifDB& ) = delete;
    GifDB& operator=( const GifDB& ) = delete;

    const gif_info& operator[]( SimpleLogger::severity_level Index )
    {
        return ( gif_data_[ size_t( Index ) ] );
    };

private:
    GifDB( )
    {
        gif_data_ = std::unique_ptr<
            gif_info[] >( new gif_info[ size_t( SimpleLogger::severity_level::
                                                    phone ) +
                                        1 ]{
            //---------------------------------------------------------------
            // 1 - OK
            //---------------------------------------------------------------
            { "ball_green.gif",
              "NORMAL",
              "green ball",
              "Normal status or debugging message",
              14,
              12,
              gif_info::ALT_TIME },
            //---------------------------------------------------------------
            // 1 - MT_DEBUG
            //---------------------------------------------------------------
            { "ball_blue.gif",
              "DEBUG",
              "blue ball",
              "Debugging message intended for developers",
              14,
              12,
              gif_info::ALT_TIME },
            //---------------------------------------------------------------
            // 2 - MT_NOTE
            //---------------------------------------------------------------
            { "ball_purple.gif",
              "NOTE",
              "purple ball",
              "Notable condition which is not an error",
              14,
              12,
              gif_info::ALT_TIME },
            //---------------------------------------------------------------
            // 3 - MT_WARN
            //---------------------------------------------------------------
            { "ball_yellow.gif",
              "WARNING",
              "NOTABLE",
              "Notable condition which may be a non-fatal error",
              14,
              12,
              gif_info::ALT_TIME },
            //---------------------------------------------------------------
            // 4 - error_non_fatal
            //---------------------------------------------------------------
            { "ball_orange.gif",
              "NON-FATAL",
              "orange ball",
              "Error condition not fatal to job",
              14,
              12,
              gif_info::ALT_TIME },
            //---------------------------------------------------------------
            // 5 - MT_ERROR
            //---------------------------------------------------------------
            { "ball_red.gif",
              "FATAL",
              "ERROR",
              "Error condition which fatal to job",
              14,
              12,
              gif_info::ALT_TIME },
            //---------------------------------------------------------------
            // 6 - MT_EMAIL
            //---------------------------------------------------------------
            { "mail.gif",
              "URGENT",
              "email",
              "Condition requires email notification of the "
              "responsible "
              "administrator of this API",
              27,
              16,
              gif_info::ALT_EMAIL },
            //---------------------------------------------------------------
            // 7 - MT_PHONE
            //---------------------------------------------------------------
            { "telephone.gif",
              "CRITICAL",
              "telephone",
              "Condition requires phone notification of the "
              "responsible "
              "administrator of this API",
              27,
              27,
              gif_info::ALT_TIME }
            //
        } );
    };

    ~GifDB( ) = default;

    struct Deleter
    {
        void
        operator( )( GifDB* Pointer ) const
        {
            delete Pointer;
        };
    };

    static void
    init( )
    {
        // instance = std::make_unique< GifDB >( );
        instance.reset( new GifDB( ) );
        std::atexit( cleanup );
    };

    static void
    cleanup( )
    {
        instance.reset( );
    };

    std::unique_ptr< gif_info[] > gif_data_;

    static std::unique_ptr< GifDB, Deleter > instance;
    static std::once_flag                    initFlag;

    friend void init( );
};

std::unique_ptr< GifDB, GifDB::Deleter > GifDB::instance = nullptr;
std::once_flag                           GifDB::initFlag;

//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
static const std::string
gif_legend( SimpleLogger::severity_level Severity,
            SimpleLogger::FormatType     Format )
{
    std::ostringstream desc;

    try
    {
        const auto& gif_info = GifDB::getInstance( )[ Severity ];

        if ( Format == SimpleLogger::FormatType::Text )
        {
            desc << gif_info.s_alt_name << ": " << gif_info.s_description;
        }
        else if ( Format == SimpleLogger::FormatType::HTML )
        {
            desc << "<img src=\"" << gif_info.s_name << "\""
                 << " width=\"" << gif_info.s_width << "\" height=\""
                 << gif_info.s_height << "\""
                 << " alt=\"" << gif_info.s_alt_desc << "\" title=\""
                 << gif_info.s_alt_desc << "\""
                 << "> " << gif_info.s_description;
        }
    }
    catch ( ... )
    {
    }

    return desc.str( );
}

// Static member initialization
bool SimpleLogger::global_sink_initialized_ = false;
std::mutex SimpleLogger::global_sink_mutex_;

SimpleLogger::SimpleLogger( const std::string& channel )
    : channel_name_( channel ),
      channel_logger_( boost::log::keywords::channel = channel ),
      format_type_( FormatType::Text ), 
      debug_level_( 0 ),
      min_severity_( custom_severity_level( severity_level::debug ) )
{
    init_common_attributes( );
    
    // Only initialize console sink if global sink hasn't been initialized yet
    {
        std::lock_guard< std::mutex > lock( global_sink_mutex_ );
        if ( !global_sink_initialized_ )
        {
            init_logging( create_console_sink_internal() );
            global_sink_initialized_ = true;
        }
    }
    
    set_min_severity( min_severity_ );
}


void
SimpleLogger::set_debug_level( int level )
{
    debug_level_ = level;
}

void
SimpleLogger::set_min_severity( custom_severity_level level )
{
    min_severity_ = level;
    boost::log::core::get( )->set_filter(
        boost::log::expressions::attr< custom_severity_level >( "Severity" ) >=
        min_severity_ );
}

void
SimpleLogger::change_sink( const std::string& sink_type,
                           const std::string& dir )
{
    std::lock_guard< std::mutex > lock( mutex_ );
    boost::log::core::get( )->remove_all_sinks( );

    if ( sink_type == "console" )
    {
        init_logging( create_console_sink( ) );
    }
    else if ( sink_type == "file" )
    {
        init_logging( create_rotating_file_sink( dir ) );
    }
    else if ( sink_type == "syslog" )
    {
        init_logging( create_syslog_sink( ) );
    }
    else if ( sink_type == "null" )
    {
        init_logging( create_null_sink( ) );
    }
    else
    {
        throw std::invalid_argument( "Invalid sink type" );
    }
}

void
SimpleLogger::set_format_type( FormatType format_type )
{
    format_type_ = format_type;
    std::lock_guard< std::mutex > lock( mutex_ );
    auto                          core = boost::log::core::get( );
    core->remove_all_sinks( );
    if ( current_sink_ )
    {
        init_logging( current_sink_ );
    }
}

void
SimpleLogger::init_logging( boost::shared_ptr< boost::log::sinks::sink > sink )
{
    boost::log::core::get( )->add_sink( sink );
    current_sink_ = sink;
}

void
SimpleLogger::init_common_attributes( )
{
    boost::log::add_common_attributes( );
}

boost::shared_ptr< boost::log::sinks::sink >
SimpleLogger::create_console_sink( )
{
    auto backend =
        boost::make_shared< boost::log::sinks::text_ostream_backend >( );
    backend->add_stream( boost::shared_ptr< std::ostream >(
        &std::cerr, boost::null_deleter( ) ) );
    auto sink = boost::make_shared< boost::log::sinks::synchronous_sink<
        boost::log::sinks::text_ostream_backend > >( backend );
    apply_formatter( sink );
    return sink;
}

boost::shared_ptr< boost::log::sinks::sink >
SimpleLogger::create_console_sink_internal( )
{
    // Internal version for first-time creation only
    auto backend =
        boost::make_shared< boost::log::sinks::text_ostream_backend >( );
    backend->add_stream( boost::shared_ptr< std::ostream >(
        &std::cerr, boost::null_deleter( ) ) );
    auto sink = boost::make_shared< boost::log::sinks::synchronous_sink<
        boost::log::sinks::text_ostream_backend > >( backend );
    apply_formatter( sink );
    return sink;
}

boost::shared_ptr< boost::log::sinks::sink >
SimpleLogger::create_rotating_file_sink( const std::string& dir )
{
    std::string log_dir = dir.empty( ) ? "." : dir;
    if ( !std::filesystem::exists( log_dir ) )
    {
        std::filesystem::create_directories( log_dir );
    }
    auto backend = boost::make_shared< boost::log::sinks::text_file_backend >(
        boost::log::keywords::file_name = log_dir + "/sample_%N.log",
        boost::log::keywords::rotation_size = 10 * 1024 * 1024 );

    backend->set_open_handler( [this]( std::ostream& os ) {
        std::string application_name( "LDAS Tools Suite Application" );
        std::string host( "?host?" );
        std::string site( "?site?" );
        std::string special_log_header( "" );
        std::string title;

        title = "The ";
        title += application_name;
        title += " API on ";
        title += host;
        title += " at ";
        title += site;

        if ( format_type_ == FormatType::Text )
        {
            os << " ****** " << title << " ******" << std::endl //
               << " ------ Legend: ------" << std::endl //
                ;
        }
        else if ( format_type_ == FormatType::HTML )
        {
            os << "<HTML> " << std::endl //
               << "<HEAD> " << std::endl //
               << "<TITLE>" << title << "</TITLE> " << std::endl //
               << "<BODY BGCOLOR=\"#DDDDDD\" TEXT=\"#000000\"> " << std::endl //
               << "<h3>" << title << "</h3> " << std::endl //
               << "<b><i>Legend:</i></b><br> " << std::endl //
                ;
        }
        os << //
            gif_legend( SimpleLogger::severity_level::ok, format_type_ )
           << std::endl //
           << gif_legend( SimpleLogger::severity_level::warning, format_type_ )
           << std::endl //
           << gif_legend( SimpleLogger::severity_level::error_non_fatal,
                          format_type_ )
           << std::endl //
           << gif_legend( SimpleLogger::severity_level::error, format_type_ )
           << std::endl //
           << gif_legend( SimpleLogger::severity_level::debug, format_type_ )
           << std::endl //
           << gif_legend( SimpleLogger::severity_level::note, format_type_ )
           << std::endl //
           << gif_legend( SimpleLogger::severity_level::email, format_type_ )
           << std::endl //
           << gif_legend( SimpleLogger::severity_level::phone, format_type_ )
           << std::endl //
            ;
        if ( format_type_ == FormatType::Text )
        {
            os << std::endl;
        }
        else if ( format_type_ == FormatType::HTML )
        {
            if ( !special_log_header.empty( ) )
            {
                os << special_log_header << std::endl;
            }
            os //
                << "<p><b><i>Link:</i></b> " << std::endl //
                << "<a href=\"APIstatus.html\">API Status Page for " << site
                << "</a> " << std::endl //
                << "<p>" << std::endl;
        }
    } );

    backend->set_close_handler( [this]( std::ostream& os ) {
        if ( format_type_ == FormatType::Text )
        {
            os << "----- Log file closed -----" << std::endl;
            os << "This block of text is added to each log file when it is "
                  "closed."
               << std::endl;
            os << "----------------------------" << std::endl;
        }
        else if ( format_type_ == FormatType::HTML )
        {
            os << "</BODY>" << std::endl //
               << "</HTML>" << std::endl //
                ;
        }
    } );

    auto sink = boost::make_shared< boost::log::sinks::synchronous_sink<
        boost::log::sinks::text_file_backend > >( backend );
    apply_formatter( sink );
    return sink;
}

boost::shared_ptr< boost::log::sinks::sink >
SimpleLogger::create_syslog_sink( )
{
    auto backend = boost::make_shared< boost::log::sinks::syslog_backend >(
        boost::log::keywords::facility = boost::log::sinks::syslog::user );
    auto sink = boost::make_shared< boost::log::sinks::synchronous_sink<
        boost::log::sinks::syslog_backend > >( backend );
    apply_formatter( sink );
    return sink;
}

boost::shared_ptr< boost::log::sinks::sink >
SimpleLogger::create_null_sink( )
{
    auto sink = boost::make_shared<
        boost::log::sinks::synchronous_sink< null_sink > >( );
    return sink;
}

void
SimpleLogger::apply_formatter(
    boost::shared_ptr< boost::log::sinks::synchronous_sink<
        boost::log::sinks::text_ostream_backend > >& sink )
{
    if ( format_type_ == FormatType::HTML )
    {
        sink->set_formatter(
            boost::log::expressions::stream
            << "<div class=\"log-entry\">"
            << "<span class=\"severity\">"
            << boost::log::expressions::attr< custom_severity_level >(
                   "Severity" )
            << "</span>"
            << ": " << timestamp_str( ) //
            << " <span class=\"message\">" << boost::log::expressions::smessage
            << "</span>"
            << "</div>" //
        );
    }
    else
    {
        sink->set_formatter(
            boost::log::expressions::stream
            << "["
            << boost::log::expressions::attr< custom_severity_level >(
                   "Severity" )
            << "] " //
            << timestamp_str( ) << " " //
            << boost::log::expressions::smessage //
        );
    }
}

void
SimpleLogger::apply_formatter(
    boost::shared_ptr< boost::log::sinks::synchronous_sink<
        boost::log::sinks::text_file_backend > >& sink )
{
    if ( format_type_ == FormatType::HTML )
    {
        sink->set_formatter(
            boost::log::expressions::stream
            << "<div class=\"log-entry\">"
            << "<span class=\"severity\">"
            << boost::log::expressions::attr< custom_severity_level >(
                   "Severity" )
            << "</span>"
            << ": " << timestamp_str( ) //
            << " <span class=\"message\">" << boost::log::expressions::smessage
            << "</span>"
            << "</div>" //
        );
    }
    else
    {
        sink->set_formatter(
            boost::log::expressions::stream
            << "["
            << boost::log::expressions::attr< custom_severity_level >(
                   "Severity" )
            << "] " //
            << timestamp_str( ) //
            << boost::log::expressions::smessage //
        );
    }
}

void
SimpleLogger::apply_formatter(
    boost::shared_ptr< boost::log::sinks::synchronous_sink<
        boost::log::sinks::syslog_backend > >& sink )
{
    if ( format_type_ == FormatType::HTML )
    {
        sink->set_formatter(
            boost::log::expressions::stream
            << "<div class=\"log-entry\">"
            << "<span class=\"severity\">"
            << boost::log::expressions::attr< custom_severity_level >(
                   "Severity" )
            << "</span>"
            << ": " << timestamp_str( ) //
            << "<span class=\"message\">" << boost::log::expressions::smessage
            << "</span>"
            << "</div>" //
        );
    }
    else
    {
        sink->set_formatter(
            boost::log::expressions::stream
            << "["
            << boost::log::expressions::attr< custom_severity_level >(
                   "Severity" )
            << "] " //
            << timestamp_str( ) << " " //
            << boost::log::expressions::smessage //
        );
    }
}

std::string
SimpleLogger::timestamp_str( ) const
{
    LDASTools::AL::GPSTime timestamp;

    timestamp.Now( );

    std::ostringstream oss;

    oss << timestamp.GetSeconds( );

    return oss.str( );
}

boost::shared_ptr< boost::log::sinks::sink >
SimpleLogger::create_cerr_sink( )
{
    auto backend =
        boost::make_shared< boost::log::sinks::text_ostream_backend >( );
    backend->add_stream( boost::shared_ptr< std::ostream >(
        &std::cerr, boost::null_deleter( ) ) );
    backend->auto_flush( true );  // Debug messages should flush immediately
    
    auto sink = boost::make_shared< boost::log::sinks::synchronous_sink<
        boost::log::sinks::text_ostream_backend > >( backend );
    apply_formatter( sink );
    return sink;
}

boost::shared_ptr< boost::log::sinks::sink >
SimpleLogger::create_cout_sink( )
{
    auto backend =
        boost::make_shared< boost::log::sinks::text_ostream_backend >( );
    backend->add_stream( boost::shared_ptr< std::ostream >(
        &std::cout, boost::null_deleter( ) ) );
    
    auto sink = boost::make_shared< boost::log::sinks::synchronous_sink<
        boost::log::sinks::text_ostream_backend > >( backend );
    apply_formatter( sink );
    return sink;
}

void
SimpleLogger::add_filtered_sink(boost::shared_ptr< boost::log::sinks::sink > sink,
                               custom_severity_level min_severity,
                               custom_severity_level max_severity)
{
    std::lock_guard< std::mutex > lock( mutex_ );
    
    // Cast to synchronous sink to access set_filter method
    if (auto sync_sink = boost::dynamic_pointer_cast<
            boost::log::sinks::synchronous_sink<boost::log::sinks::text_ostream_backend>>(sink)) {
        sync_sink->set_filter(
            boost::log::expressions::attr< custom_severity_level >( "Severity" ) >= min_severity &&
            boost::log::expressions::attr< custom_severity_level >( "Severity" ) <= max_severity &&
            boost::log::expressions::attr< std::string >( "Channel" ) == channel_name_
        );
    } else if (auto file_sink = boost::dynamic_pointer_cast<
                   boost::log::sinks::synchronous_sink<boost::log::sinks::text_file_backend>>(sink)) {
        file_sink->set_filter(
            boost::log::expressions::attr< custom_severity_level >( "Severity" ) >= min_severity &&
            boost::log::expressions::attr< custom_severity_level >( "Severity" ) <= max_severity &&
            boost::log::expressions::attr< std::string >( "Channel" ) == channel_name_
        );
    }
    
    boost::log::core::get( )->add_sink( sink );
    sinks_.push_back( sink );
}

void
SimpleLogger::setup_optimized_routing()
{
    std::lock_guard< std::mutex > lock( mutex_ );
    
    // Clear existing sinks
    auto core = boost::log::core::get( );
    for (auto& sink : sinks_) {
        core->remove_sink( sink );
    }
    sinks_.clear();
    
    // For now, use a simple approach - debug to cerr, others to cout
    // Advanced filtering can be added later with core-level filters
    
    // Debug messages to cerr (will be filtered by debug_level_ in template methods)
    auto cerr_sink = create_cerr_sink();
    core->add_sink( cerr_sink );
    sinks_.push_back( cerr_sink );
    
    // Info/warnings to cout 
    auto cout_sink = create_cout_sink();
    core->add_sink( cout_sink );
    sinks_.push_back( cout_sink );
    
    // Errors to rotating file for persistence
    auto error_file_sink = create_rotating_file_sink( "logs" );
    core->add_sink( error_file_sink );
    sinks_.push_back( error_file_sink );
}

void
SimpleLogger::set_custom_sink(boost::shared_ptr<boost::log::sinks::sink> sink)
{
    std::lock_guard< std::mutex > lock( mutex_ );
    
    // Replace all existing sinks with the custom sink
    boost::log::core::get()->remove_all_sinks();
    boost::log::core::get()->add_sink(sink);
    
    // Clear our internal sink tracking since we're using external management
    sinks_.clear();
    current_sink_ = sink;
}

void
SimpleLogger::set_global_custom_sink(boost::shared_ptr<boost::log::sinks::sink> sink)
{
    // Thread-safe global sink replacement
    std::lock_guard< std::mutex > lock( global_sink_mutex_ );
    
    if (sink) {
        // Replace all existing sinks with the custom sink
        boost::log::core::get()->remove_all_sinks();
        boost::log::core::get()->add_sink(sink);
        
        // Mark global sink as initialized so future loggers use this setup
        global_sink_initialized_ = true;
    } else {
        // Clear global custom sink - future loggers will use default setup
        global_sink_initialized_ = false;
    }
}

