#include "arena.hpp"
#include "format.hpp"
#include <cstdio>
#include <iomanip>
#include <nanobench.h>
#include <sstream>
#include <string>

#if __cpp_lib_format >= 201907L
#include <format>
#define HAS_STD_FORMAT 1
#else
#define HAS_STD_FORMAT 0
#endif

// Test data for consistent benchmarks
constexpr int TEST_INT = 42;
constexpr double TEST_DOUBLE =
    3.141592653589793; // Exact IEEE 754 representation of π
const std::string TEST_STRING = "Hello World";

// Benchmark simple string concatenation: "Hello " + "World" + "!"
void benchmark_simple_concatenation() {
  std::cout << "\n=== Simple String Concatenation: 'Hello World!' ===\n";

  ankerl::nanobench::Bench bench;
  bench.title("Simple Concatenation").unit("op").warmup(100);

  Arena arena(64);
  // Arena-based static_format
  bench.run("static_format", [&] {
    auto result = static_format(arena, "Hello ", "World", "!");
    ankerl::nanobench::doNotOptimizeAway(result);
    arena.reset();
  });

  // Arena-based format
  bench.run("format", [&] {
    auto result = format(arena, "Hello %s!", "World");
    ankerl::nanobench::doNotOptimizeAway(result);
    arena.reset();
  });

  // std::stringstream
  bench.run("std::stringstream", [&] {
    std::stringstream ss;
    ss << "Hello " << "World" << "!";
    auto result = ss.str();
    ankerl::nanobench::doNotOptimizeAway(result);
  });

#if HAS_STD_FORMAT
  // std::format (C++20)
  bench.run("std::format", [&] {
    auto result = std::format("Hello {}!", "World");
    ankerl::nanobench::doNotOptimizeAway(result);
  });
#endif
}

// Benchmark mixed type formatting: "Count: 42, Rate: 3.14159"
void benchmark_mixed_types() {
  std::cout << "\n=== Mixed Type Formatting: 'Count: 42, Rate: 3.14159' ===\n";

  ankerl::nanobench::Bench bench;
  bench.title("Mixed Types").unit("op").warmup(100);

  Arena arena(128);
  // Arena-based static_format
  bench.run("static_format", [&] {
    auto result =
        static_format(arena, "Count: ", TEST_INT, ", Rate: ", TEST_DOUBLE);
    ankerl::nanobench::doNotOptimizeAway(result);
    arena.reset();
  });

  // Arena-based format
  bench.run("format", [&] {
    auto result = format(arena, "Count: %d, Rate: %.5f", TEST_INT, TEST_DOUBLE);
    ankerl::nanobench::doNotOptimizeAway(result);
    arena.reset();
  });

  // std::stringstream
  bench.run("std::stringstream", [&] {
    std::stringstream ss;
    ss << "Count: " << TEST_INT << ", Rate: " << std::fixed
       << std::setprecision(5) << TEST_DOUBLE;
    auto result = ss.str();
    ankerl::nanobench::doNotOptimizeAway(result);
  });

#if HAS_STD_FORMAT
  // std::format (C++20)
  bench.run("std::format", [&] {
    auto result = std::format("Count: {}, Rate: {:.5f}", TEST_INT, TEST_DOUBLE);
    ankerl::nanobench::doNotOptimizeAway(result);
  });
#endif
}

// Benchmark complex formatting with precision and alignment
void benchmark_complex_formatting() {
  std::cout << "\n=== Complex Formatting: '%-10s %5d %8.2f' ===\n";

  ankerl::nanobench::Bench bench;
  bench.title("Complex Formatting").unit("op").warmup(100);

  Arena arena(128);
  // Arena-based format (static_format doesn't support printf specifiers)
  bench.run("format", [&] {
    auto result = format(arena, "%-10s %5d %8.2f", TEST_STRING.c_str(),
                         TEST_INT, TEST_DOUBLE);
    ankerl::nanobench::doNotOptimizeAway(result);
    arena.reset();
  });

  // std::stringstream
  bench.run("std::stringstream", [&] {
    std::stringstream ss;
    ss << std::left << std::setw(10) << TEST_STRING << " " << std::right
       << std::setw(5) << TEST_INT << " " << std::setw(8) << std::fixed
       << std::setprecision(2) << TEST_DOUBLE;
    auto result = ss.str();
    ankerl::nanobench::doNotOptimizeAway(result);
  });

#if HAS_STD_FORMAT
  // std::format (C++20)
  bench.run("std::format", [&] {
    auto result = std::format("{:<10} {:>5} {:>8.2f}", TEST_STRING, TEST_INT,
                              TEST_DOUBLE);
    ankerl::nanobench::doNotOptimizeAway(result);
  });
#endif
}

// Benchmark error message formatting (common use case)
void benchmark_error_messages() {
  std::cout << "\n=== Error Message Formatting: 'Error 404: File not found "
               "(line 123)' ===\n";

  ankerl::nanobench::Bench bench;
  bench.title("Error Messages").unit("op").warmup(100);

  constexpr int error_code = 404;
  constexpr int line_number = 123;
  const std::string error_msg = "File not found";

  Arena arena(128);
  // Arena-based static_format (using string literals only)
  bench.run("static_format", [&] {
    auto result = static_format(arena, "Error ", error_code, ": ",
                                "File not found", " (line ", line_number, ")");
    ankerl::nanobench::doNotOptimizeAway(result);
    arena.reset();
  });

  // Arena-based format
  bench.run("format", [&] {
    auto result = format(arena, "Error %d: %s (line %d)", error_code,
                         error_msg.c_str(), line_number);
    ankerl::nanobench::doNotOptimizeAway(result);
    arena.reset();
  });

  // std::stringstream
  bench.run("std::stringstream", [&] {
    std::stringstream ss;
    ss << "Error " << error_code << ": " << error_msg << " (line "
       << line_number << ")";
    auto result = ss.str();
    ankerl::nanobench::doNotOptimizeAway(result);
  });

#if HAS_STD_FORMAT
  // std::format (C++20)
  bench.run("std::format", [&] {
    auto result = std::format("Error {}: {} (line {})", error_code, error_msg,
                              line_number);
    ankerl::nanobench::doNotOptimizeAway(result);
  });
#endif
}

// Benchmark simple double formatting (common in metrics)
void benchmark_double_formatting() {
  std::cout << "\n=== Simple Double Formatting ===\n";

  // Validate that all formatters produce identical output
  Arena arena(128);

  auto static_result = static_format(arena, TEST_DOUBLE);
  auto format_result = format(arena, "%.17g", TEST_DOUBLE);

  std::stringstream ss;
  ss << std::setprecision(17) << TEST_DOUBLE;
  auto stringstream_result = ss.str();

#if HAS_STD_FORMAT
  auto std_format_result = std::format("{}", TEST_DOUBLE);
#endif

  std::cout << "Validation (note: precision algorithms may differ):\n";
  std::cout << "  static_format:   '" << static_result
            << "' (length: " << static_result.length() << ")\n";
  std::cout << "  format(%.17g):   '" << format_result
            << "' (length: " << format_result.length() << ")\n";
  std::cout << "  std::stringstream: '" << stringstream_result
            << "' (length: " << stringstream_result.length() << ")\n";
#if HAS_STD_FORMAT
  std::cout << "  std::format:     '" << std_format_result
            << "' (length: " << std_format_result.length() << ")\n";
#endif

  std::cout
      << "Note: Different formatters may use different precision algorithms\n";
  std::cout << "Proceeding with performance comparison...\n";

  ankerl::nanobench::Bench bench;
  bench.title("Double Formatting").unit("op").warmup(100);

  // Arena-based static_format (double only)
  bench.run("static_format(double)", [&] {
    auto result = static_format(arena, TEST_DOUBLE);
    ankerl::nanobench::doNotOptimizeAway(result);
    arena.reset();
  });

  // Arena-based format with equivalent precision
  bench.run("format(%.17g)", [&] {
    // Use %.17g to match static_format's full precision behavior
    auto result = format(arena, "%.17g", TEST_DOUBLE);
    ankerl::nanobench::doNotOptimizeAway(result);
    arena.reset();
  });

  // std::stringstream (full precision)
  bench.run("std::stringstream", [&] {
    std::stringstream ss;
    ss << std::setprecision(17) << TEST_DOUBLE;
    auto result = ss.str();
    ankerl::nanobench::doNotOptimizeAway(result);
  });

#if HAS_STD_FORMAT
  // std::format (C++20) - default formatting
  bench.run("std::format", [&] {
    auto result = std::format("{}", TEST_DOUBLE);
    ankerl::nanobench::doNotOptimizeAway(result);
  });
#endif
}

int main() {
  std::cout << "Format Function Benchmark Comparison\n";
  std::cout << "====================================\n";

#if HAS_STD_FORMAT
  std::cout << "C++20 std::format: Available\n";
#else
  std::cout << "C++20 std::format: Not available\n";
#endif

  benchmark_simple_concatenation();
  benchmark_mixed_types();
  benchmark_complex_formatting();
  benchmark_error_messages();
  benchmark_double_formatting();

  std::cout << "\n=== Summary ===\n";
  std::cout
      << "* static_format: Best for simple concatenation with known types\n";
  std::cout
      << "* format: Best for printf-style formatting with arena allocation\n";
  std::cout
      << "* std::stringstream: Flexible but slower due to heap allocation\n";
  std::cout << "* std::format: Modern C++20 alternative (if available)\n";

  return 0;
}
