#include "spdlog/spdlog.h"
#include "StockData_Old.h"
#include "StockData_Modern.h"
#include "Trade.h"
#include "TickData.h"
#include "CsvParser.h"

#include <memory>     // For smart pointers
#include <iostream>   // For std::cout, std::cerr
#include <vector>     // For std::vector
#include <numeric>    // For std::accumulate
#include <algorithm>  // For std::max_element, std::for_each, std::count_if
#include <map>        // For std::map (instead of unordered_map for simplicity in example)
#include <string_view> // For std::string_view
#include <fstream>    // For std::ofstream

// --- Existing Tutorial 2.1 Demonstrations ---

void demonstrate_old_way() {
    spdlog::info("--- Demonstrating the Old Way (Manual Memory Management) ---");
    StockData_Old prices(5);
    for (size_t i = 0; i < 5; ++i) {
        prices.at(i) = 100.0 + i;
    }

    spdlog::info("Copied prices_copy1(prices)");
    StockData_Old prices_copy1(prices); // Copy constructor
    prices_copy1.at(0) = 999.0;

    spdlog::info("prices.at(0) = {}", prices.at(0));
    spdlog::info("prices_copy1.at(0) = {}", prices_copy1.at(0));

    spdlog::info("Copied prices_copy2 = prices");
    StockData_Old prices_copy2(1);
    prices_copy2 = prices; // Copy assignment
    prices_copy2.at(1) = 888.0;

    spdlog::info("prices.at(1) = {}", prices.at(1));
    spdlog::info("prices_copy2.at(1) = {}", prices_copy2.at(1));

    spdlog::info("--- Leaving scope for Old Way demo ---");
}

void demonstrate_modern_way() {
    spdlog::info("--- Demonstrating the Modern Way (RAII with std::vector) ---");
    StockData_Modern prices(5);
    for (size_t i = 0; i < 5; ++i) {
        prices.at(i) = 100.0 + i;
    }

    spdlog::info("Copied prices_copy1(prices)");
    StockData_Modern prices_copy1(prices); // Copy constructor (compiler-generated)
    prices_copy1.at(0) = 999.0;

    spdlog::info("prices.at(0) = {}", prices.at(0));
    spdlog::info("prices_copy1.at(0) = {}", prices_copy1.at(0));

    spdlog::info("Copied prices_copy2 = prices");
    StockData_Modern prices_copy2(1);
    prices_copy2 = prices; // Copy assignment (compiler-generated)
    prices_copy2.at(1) = 888.0;

    spdlog::info("prices.at(1) = {}", prices.at(1));
    spdlog::info("prices_copy2.at(1) = {}", prices_copy2.at(1));
    spdlog::info("--- Leaving scope for Modern Way demo ---");
}

void demonstrate_smart_pointers() {
    spdlog::info("--- Demonstrating Smart Pointers ---");

    // Demonstrate unique_ptr for exclusive ownership
    {
        spdlog::info("Creating a unique_ptr-managed Trade");
        auto unique_trade = std::make_unique<Trade>("AAPL", 150.0);
        unique_trade->print();
        // unique_trade is automatically destroyed when this scope ends
    } // ~Trade() is called here

    // Demonstrate shared_ptr for shared ownership
    {
        spdlog::info("Creating a shared_ptr-managed Trade");
        std::shared_ptr<Trade> shared_trade1;
        {
            auto shared_trade2 = std::make_shared<Trade>("GOOG", 2800.0);
            shared_trade1 = shared_trade2; // Both pointers now share ownership
            spdlog::info("shared_ptr use_count: {}", shared_trade1.use_count());
            shared_trade2->print();
        } // shared_trade2 goes out of scope, but the object is not destroyed

        spdlog::info("shared_ptr use_count after inner scope: {}", shared_trade1.use_count());
        shared_trade1->print();
        // shared_trade1 is automatically destroyed when this scope ends
    } // ~Trade() is called here
    spdlog::info("--- Leaving scope for Smart Pointers demo ---");
}

// --- New Tutorial 2.2 Demonstrations ---

void generate_mock_csv_file(std::string_view filename, int num_lines) {
    spdlog::info("Generating mock CSV file: {}", std::string(filename));
    std::ofstream ofs(std::string{filename});
    if (!ofs.is_open()) {
        throw std::runtime_error("Could not create mock CSV file.");
    }

    for (int i = 0; i < num_lines; ++i) {
        long long timestamp = std::chrono::system_clock::now().time_since_epoch().count() / 1000000000LL + i;
        double price = 100.0 + (i * 0.5);
        long volume = 100 + (i * 10);
        ofs << timestamp << "," << price << "," << volume << "\n";
    }
    ofs.close();
}


void demonstrate_stl_containers_algorithms() {
    spdlog::info("--- Demonstrating Modern STL Containers & Algorithms (Tutorial 2.2) ---");

    const std::string_view csv_filename = "mock_tick_data.csv";
    generate_mock_csv_file(csv_filename, 10);

    std::vector<TickData> ticks;
    try {
        ticks = CsvParser::parseTickCsvFile(csv_filename);
        spdlog::info("Successfully parsed {} ticks.", ticks.size());
    } catch (const std::runtime_error& e) {
        spdlog::error("Error parsing CSV file: {}", e.what());
        return;
    }

    // Example 1: Find the highest price
    auto max_price_it = std::max_element(ticks.begin(), ticks.end(),
                                         [](const TickData& a, const TickData& b) {
                                             return a.price < b.price;
                                         });
    if (max_price_it != ticks.end()) {
        spdlog::info("Highest price found: {}", max_price_it->price);
    }

    // Example 2: Calculate total volume
    long total_volume = std::accumulate(ticks.begin(), ticks.end(), 0LL,
                                        [](long sum, const TickData& tick) {
                                            return sum + tick.volume;
                                        });
    spdlog::info("Total volume: {}", total_volume);

    // Example 3: Filter ticks above a certain price (e.g., > 102.0)
    std::vector<TickData> high_price_ticks;
    std::copy_if(ticks.begin(), ticks.end(), std::back_inserter(high_price_ticks),
                 [](const TickData& tick) {
                     return tick.price > 102.0;
                 });
    spdlog::info("Number of ticks with price > 102.0: {}", high_price_ticks.size());
    // std::for_each(high_price_ticks.begin(), high_price_ticks.end(),
    //               [](const TickData& tick) { spdlog::info("  {}", tick.toString()); });

    // Example 4: Average price using a lambda
    double sum_prices = 0.0;
    std::for_each(ticks.begin(), ticks.end(),
                  [&sum_prices](const TickData& tick) {
                      sum_prices += tick.price;
                  });
    if (!ticks.empty()) {
        spdlog::info("Average price: {}", sum_prices / ticks.size());
    }

    // Example 5: Aggregate volume by second (using std::map for simplicity)
    // For high performance, a custom hash map or a more direct time-series approach would be used.
    std::map<long long, long> volume_by_second;
    for (const auto& tick : ticks) {
        long long sec = std::chrono::duration_cast<std::chrono::seconds>(tick.timestamp.time_since_epoch()).count();
        volume_by_second[sec] += tick.volume;
    }

    spdlog::info("Volume by second (first 3 entries):");
    int count = 0;
    for (const auto& entry : volume_by_second) {
        if (count >= 3) break;
        spdlog::info("  Timestamp (sec): {}, Volume: {}", entry.first, entry.second);
        count++;
    }

    spdlog::info("--- Leaving scope for STL Containers & Algorithms demo ---");
}


int main() {
    spdlog::set_level(spdlog::level::info);
    spdlog::info("Starting Tutorial 2: Core Language, Libraries, and Concepts");

    demonstrate_old_way();
    std::cout << "\n";
    demonstrate_modern_way();
    std::cout << "\n";
    demonstrate_smart_pointers();
    std::cout << "\n";
    demonstrate_stl_containers_algorithms();


    spdlog::info("Tutorial 2 finished.");
    return 0;
}