#include <algorithm>
#include <functional>
#include <iterator>
#include <list>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include <gtest/gtest.h>

#include "mcp/core/memory_utils.h"
#include "mcp/core/optional.h"

// Test suite inspired by C++17 std::optional test patterns
// Tests edge cases, SFINAE, and complex type interactions

namespace {

// Helper types for testing
struct Implicit {
  int value;
  Implicit(int v) : value(v) {}  // Implicit conversion from int
};

struct Explicit {
  int value;
  explicit Explicit(int v) : value(v) {}  // Explicit conversion only
};

struct MultiArg {
  int a;
  double b;
  std::string c;
  MultiArg(int x, double y, const std::string& z) : a(x), b(y), c(z) {}
};

// Type with throwing copy operations
struct ThrowingCopy {
  static bool should_throw;
  int value;

  ThrowingCopy(int v = 0) : value(v) {
    if (should_throw)
      throw std::runtime_error("construct");
  }

  ThrowingCopy(const ThrowingCopy& other) : value(other.value) {
    if (should_throw)
      throw std::runtime_error("copy construct");
  }

  ThrowingCopy(ThrowingCopy&& other) : value(other.value) { other.value = -1; }

  ThrowingCopy& operator=(const ThrowingCopy& other) {
    if (should_throw)
      throw std::runtime_error("copy assign");
    value = other.value;
    return *this;
  }

  ThrowingCopy& operator=(ThrowingCopy&& other) {
    value = other.value;
    other.value = -1;
    return *this;
  }
};
bool ThrowingCopy::should_throw = false;

// Type that tracks its state
struct StateTracker {
  enum State { DEFAULT, COPY, MOVE, DEAD };
  mutable State state;

  StateTracker() : state(DEFAULT) {}
  StateTracker(const StateTracker&) : state(COPY) {}
  StateTracker(StateTracker&& other) : state(MOVE) { other.state = DEAD; }
  ~StateTracker() { state = DEAD; }

  StateTracker& operator=(const StateTracker&) {
    state = COPY;
    return *this;
  }
  StateTracker& operator=(StateTracker&& other) {
    state = MOVE;
    other.state = DEAD;
    return *this;
  }
};

// Type with deleted operations
struct NoDefault {
  NoDefault() = delete;
  explicit NoDefault(int) {}
};

struct NoCopy {
  NoCopy() = default;
  NoCopy(const NoCopy&) = delete;
  NoCopy& operator=(const NoCopy&) = delete;
  NoCopy(NoCopy&&) = default;
  NoCopy& operator=(NoCopy&&) = default;
};

// Type that's convertible to optional
struct ConvertibleToOptional {
  operator mcp::optional<int>() const { return mcp::optional<int>(42); }
};

// Aggregate type (C++11)
struct Aggregate {
  int x;
  double y;
};

// Type with overloaded address-of operator
struct EvilType {
  int value;
  EvilType(int v = 0) : value(v) {}
  EvilType(const EvilType& other) : value(other.value) {}
  EvilType& operator=(const EvilType& other) {
    value = other.value;
    return *this;
  }
  int operator&() const { return 999; }  // Evil!
};

// Helper to test SFINAE
template <typename T, typename = void>
struct is_copy_constructible_helper : std::false_type {};

template <typename T>
struct is_copy_constructible_helper<
    T,
    typename std::enable_if<
        std::is_same<T, decltype(T(std::declval<const T&>()))>::value>::type>
    : std::true_type {};

}  // namespace

class OptionalExtensiveTest : public ::testing::Test {
 protected:
  void SetUp() override { ThrowingCopy::should_throw = false; }
};

// Construction tests
TEST_F(OptionalExtensiveTest, DefaultConstructible) {
  EXPECT_TRUE(std::is_default_constructible<mcp::optional<int>>::value);
  EXPECT_TRUE(std::is_default_constructible<mcp::optional<NoDefault>>::value);

  mcp::optional<NoDefault> opt;
  EXPECT_FALSE(opt.has_value());
}

TEST_F(OptionalExtensiveTest, ConstructFromValue) {
  // Implicit conversion
  {
    mcp::optional<Implicit> opt = 42;  // Should work
    EXPECT_TRUE(opt);
    EXPECT_EQ(opt->value, 42);
  }

  // Explicit conversion
  {
    mcp::optional<Explicit> opt(42);  // Should work
    EXPECT_TRUE(opt);
    EXPECT_EQ(opt->value, 42);

    // This should not compile:
    // mcp::optional<Explicit> opt2 = 42;  // Error: explicit constructor
  }
}

TEST_F(OptionalExtensiveTest, InPlaceConstruction) {
  // Single argument
  {
    mcp::optional<std::vector<int>> opt(mcp::in_place, 10, 42);
    EXPECT_TRUE(opt);
    EXPECT_EQ(opt->size(), 10u);
    EXPECT_EQ((*opt)[0], 42);
  }

  // Multiple arguments
  {
    mcp::optional<MultiArg> opt(mcp::in_place, 1, 2.5, "test");
    EXPECT_TRUE(opt);
    EXPECT_EQ(opt->a, 1);
    EXPECT_DOUBLE_EQ(opt->b, 2.5);
    EXPECT_EQ(opt->c, "test");
  }

  // Initializer list
  {
    mcp::optional<std::set<int>> opt(mcp::in_place, {3, 1, 4, 1, 5});
    EXPECT_TRUE(opt);
    EXPECT_EQ(opt->size(), 4u);   // Duplicates removed
    EXPECT_EQ(*opt->begin(), 1);  // Sorted
  }
}

TEST_F(OptionalExtensiveTest, CopyMoveSemantics) {
  // Copy construction
  {
    mcp::optional<StateTracker> opt1(mcp::in_place);
    EXPECT_EQ(opt1->state, StateTracker::DEFAULT);

    mcp::optional<StateTracker> opt2(opt1);
    EXPECT_EQ(opt2->state, StateTracker::COPY);
    EXPECT_EQ(opt1->state, StateTracker::DEFAULT);  // Original unchanged
  }

  // Move construction
  {
    mcp::optional<StateTracker> opt1(mcp::in_place);
    mcp::optional<StateTracker> opt2(std::move(opt1));
    EXPECT_EQ(opt2->state, StateTracker::MOVE);
    EXPECT_TRUE(opt1);                           // Still has value
    EXPECT_EQ(opt1->state, StateTracker::DEAD);  // But moved from
  }

  // Copy assignment
  {
    mcp::optional<StateTracker> opt1(mcp::in_place);
    mcp::optional<StateTracker> opt2;
    opt2 = opt1;
    EXPECT_EQ(opt2->state, StateTracker::COPY);
  }

  // Move assignment
  {
    mcp::optional<StateTracker> opt1(mcp::in_place);
    mcp::optional<StateTracker> opt2;
    opt2 = std::move(opt1);
    EXPECT_EQ(opt2->state, StateTracker::MOVE);
  }
}

// Assignment tests
TEST_F(OptionalExtensiveTest, AssignmentCombinations) {
  // empty = empty
  {
    mcp::optional<int> opt1, opt2;
    opt1 = opt2;
    EXPECT_FALSE(opt1);
    EXPECT_FALSE(opt2);
  }

  // empty = full
  {
    mcp::optional<int> opt1, opt2(42);
    opt1 = opt2;
    EXPECT_TRUE(opt1);
    EXPECT_EQ(*opt1, 42);
  }

  // full = empty
  {
    mcp::optional<int> opt1(42), opt2;
    opt1 = opt2;
    EXPECT_FALSE(opt1);
  }

  // full = full
  {
    mcp::optional<int> opt1(42), opt2(99);
    opt1 = opt2;
    EXPECT_TRUE(opt1);
    EXPECT_EQ(*opt1, 99);
  }
}

TEST_F(OptionalExtensiveTest, ConvertingAssignment) {
  // Different but convertible types
  {
    mcp::optional<std::string> opt;
    opt = "hello";  // const char* -> std::string
    EXPECT_TRUE(opt);
    EXPECT_EQ(*opt, "hello");
  }

  // User-defined conversions
  {
    mcp::optional<Implicit> opt;
    opt = 42;
    EXPECT_TRUE(opt);
    EXPECT_EQ(opt->value, 42);
  }
}

// Exception safety tests
TEST_F(OptionalExtensiveTest, ExceptionSafetyConstruction) {
  ThrowingCopy::should_throw = true;

  // Construction from throwing value
  try {
    ThrowingCopy tc(42);  // This will throw
    FAIL() << "Should have thrown";
  } catch (const std::runtime_error& e) {
    EXPECT_STREQ(e.what(), "construct");
  }

  // In-place construction with non-throwing value
  ThrowingCopy::should_throw = false;
  mcp::optional<ThrowingCopy> opt(mcp::in_place, 42);
  EXPECT_TRUE(opt);
  EXPECT_EQ(opt->value, 42);
}

TEST_F(OptionalExtensiveTest, ExceptionSafetyAssignment) {
  // Test copy assignment with exception
  mcp::optional<ThrowingCopy> opt1(mcp::in_place, 42);
  mcp::optional<ThrowingCopy> opt2(mcp::in_place, 99);

  // Copy assignment to a full optional should throw during assignment
  ThrowingCopy::should_throw = true;
  try {
    opt1 = opt2;  // Copy assignment should call T's copy assignment
    FAIL() << "Should have thrown";
  } catch (const std::runtime_error& e) {
    EXPECT_STREQ(e.what(), "copy assign");
    // opt1 should be unchanged (strong exception guarantee)
    EXPECT_TRUE(opt1);
    EXPECT_EQ(opt1->value, 42);
    // opt2 should also be unchanged
    EXPECT_TRUE(opt2);
    EXPECT_EQ(opt2->value, 99);
  }

  // Reset for next test
  ThrowingCopy::should_throw = false;

  // Test copy assignment to empty optional
  mcp::optional<ThrowingCopy> opt3;
  ThrowingCopy::should_throw = true;
  try {
    opt3 = opt2;  // This will call copy constructor
    FAIL() << "Should have thrown during copy construction";
  } catch (const std::runtime_error& e) {
    EXPECT_STREQ(e.what(), "copy construct");
    // opt3 should remain empty
    EXPECT_FALSE(opt3);
    // opt2 should be unchanged
    EXPECT_TRUE(opt2);
    EXPECT_EQ(opt2->value, 99);
  }
}

// Comparison tests
TEST_F(OptionalExtensiveTest, ComparisonDifferentTypes) {
  mcp::optional<int> opt1(42);
  mcp::optional<long> opt2(42L);
  mcp::optional<double> opt3(42.0);

  EXPECT_TRUE(opt1 == opt2);
  EXPECT_TRUE(opt1 == opt3);
  EXPECT_TRUE(opt2 == opt3);

  mcp::optional<int> opt4(41);
  EXPECT_TRUE(opt4 < opt1);
  EXPECT_TRUE(opt4 < opt2);
  EXPECT_TRUE(opt4 < opt3);
}

TEST_F(OptionalExtensiveTest, ComparisonWithConvertibleTypes) {
  mcp::optional<std::string> opt("hello");

  EXPECT_TRUE(opt == "hello");
  EXPECT_TRUE("hello" == opt);
  EXPECT_FALSE(opt == "world");
  EXPECT_FALSE("world" == opt);

  EXPECT_TRUE(opt != "world");
  EXPECT_TRUE("world" != opt);

  EXPECT_TRUE(opt > "goodbye");
  EXPECT_TRUE("goodbye" < opt);
}

// Specialized behavior tests
TEST_F(OptionalExtensiveTest, OptionalVoid) {
  // While optional<void> is not very useful, it should compile
  // Note: Can't dereference or access value of optional<void>
  mcp::optional<void*> opt(nullptr);
  EXPECT_TRUE(opt);
  EXPECT_EQ(*opt, nullptr);
}

TEST_F(OptionalExtensiveTest, OptionalReference) {
  // optional doesn't support references directly, but can use pointers
  int x = 42;
  mcp::optional<int*> opt(&x);
  EXPECT_TRUE(opt);
  EXPECT_EQ(**opt, 42);

  **opt = 99;
  EXPECT_EQ(x, 99);
}

TEST_F(OptionalExtensiveTest, OptionalOfOptional) {
  using inner = mcp::optional<int>;
  using outer = mcp::optional<inner>;

  // All four states
  outer opt1;                // empty
  outer opt2(mcp::nullopt);  // empty
  inner empty_inner;
  outer opt3(empty_inner);  // contains empty optional
  outer opt4(inner(42));    // contains optional with value

  EXPECT_FALSE(opt1);
  EXPECT_FALSE(opt2);
  EXPECT_TRUE(opt3);
  EXPECT_FALSE(*opt3);
  EXPECT_TRUE(opt4);
  EXPECT_TRUE(*opt4);
  EXPECT_EQ(**opt4, 42);
}

// swap tests
TEST_F(OptionalExtensiveTest, SwapBehavior) {
  // Both empty
  {
    mcp::optional<int> opt1, opt2;
    opt1.swap(opt2);
    EXPECT_FALSE(opt1);
    EXPECT_FALSE(opt2);
  }

  // One empty, one full
  {
    mcp::optional<std::string> opt1, opt2("hello");
    opt1.swap(opt2);
    EXPECT_TRUE(opt1);
    EXPECT_EQ(*opt1, "hello");
    EXPECT_FALSE(opt2);
  }

  // Both full
  {
    mcp::optional<std::string> opt1("hello"), opt2("world");
    opt1.swap(opt2);
    EXPECT_EQ(*opt1, "world");
    EXPECT_EQ(*opt2, "hello");
  }

  // ADL swap
  {
    mcp::optional<int> opt1(42), opt2(99);
    using std::swap;
    swap(opt1, opt2);
    EXPECT_EQ(*opt1, 99);
    EXPECT_EQ(*opt2, 42);
  }
}

// make_optional tests
TEST_F(OptionalExtensiveTest, MakeOptionalVariations) {
  // Type deduction
  {
    auto opt1 = mcp::make_optional(42);
    static_assert(std::is_same<decltype(opt1), mcp::optional<int>>::value, "");

    auto opt2 = mcp::make_optional(std::string("hello"));
    static_assert(
        std::is_same<decltype(opt2), mcp::optional<std::string>>::value, "");
  }

  // Explicit type
  {
    auto opt = mcp::make_optional<std::vector<int>>(10, 42);
    EXPECT_EQ(opt->size(), 10u);
    EXPECT_EQ((*opt)[0], 42);
  }

  // With initializer list
  {
    auto opt = mcp::make_optional<std::set<int>>({3, 1, 4, 1, 5});
    EXPECT_EQ(opt->size(), 4u);
  }
}

// emplace tests
TEST_F(OptionalExtensiveTest, EmplaceVariations) {
  // Basic emplace
  {
    mcp::optional<std::string> opt("old");
    auto& ref = opt.emplace("new");
    EXPECT_EQ(*opt, "new");
    EXPECT_EQ(&ref, &*opt);  // Returns reference to new value
  }

  // Emplace with multiple args
  {
    mcp::optional<MultiArg> opt;
    opt.emplace(1, 2.5, "test");
    EXPECT_EQ(opt->a, 1);
    EXPECT_DOUBLE_EQ(opt->b, 2.5);
    EXPECT_EQ(opt->c, "test");
  }

  // Emplace on empty optional
  {
    mcp::optional<std::vector<int>> opt;
    opt.emplace(5, 42);
    EXPECT_EQ(opt->size(), 5u);
    EXPECT_EQ((*opt)[0], 42);
  }
}

// Edge cases and corner cases
TEST_F(OptionalExtensiveTest, EvilAddressOf) {
  mcp::optional<EvilType> opt(42);
  EXPECT_TRUE(opt);
  EXPECT_EQ((*opt).value, 42);

  // Despite evil operator&, these should work
  EvilType& ref = *opt;
  EXPECT_EQ(ref.value, 42);

  // Note: operator-> won't work with overloaded operator&
  // This is a known limitation when dealing with types that overload operator&
}

TEST_F(OptionalExtensiveTest, ConstexprBehavior) {
  // Limited constexpr support in C++11
  constexpr mcp::optional<int> opt1;
  constexpr mcp::optional<int> opt2(mcp::nullopt);
  constexpr mcp::optional<int> opt3(42);

  constexpr bool b1 = !opt1;
  constexpr bool b2 = !opt2;
  constexpr bool b3 = static_cast<bool>(opt3);

  EXPECT_TRUE(b1);
  EXPECT_TRUE(b2);
  EXPECT_TRUE(b3);
}

TEST_F(OptionalExtensiveTest, TrivialityPropagation) {
  // For trivial types, optional operations should be trivial where possible
  EXPECT_TRUE(std::is_trivially_destructible<mcp::optional<int>>::value);
  EXPECT_TRUE(std::is_trivially_destructible<mcp::optional<double>>::value);

  // For non-trivial types, optional is also non-trivial
  EXPECT_FALSE(
      std::is_trivially_destructible<mcp::optional<std::string>>::value);
  EXPECT_FALSE(
      std::is_trivially_destructible<mcp::optional<std::vector<int>>>::value);
}

// Type traits tests
TEST_F(OptionalExtensiveTest, TypeTraits) {
  // Copy constructibility
  EXPECT_TRUE(std::is_copy_constructible<mcp::optional<int>>::value);
  EXPECT_TRUE(std::is_copy_constructible<mcp::optional<std::string>>::value);
  // Note: C++11 optional doesn't have SFINAE-friendly copy/move operations
  // This is consistent with many C++11 implementations. The operations exist
  // but will fail to compile if used with non-copyable types.

  // Move constructibility
  EXPECT_TRUE(std::is_move_constructible<mcp::optional<int>>::value);
  EXPECT_TRUE(
      std::is_move_constructible<mcp::optional<std::unique_ptr<int>>>::value);

  // Nothrow properties
  EXPECT_TRUE(std::is_nothrow_move_constructible<mcp::optional<int>>::value);
  EXPECT_FALSE(
      std::is_nothrow_move_constructible<mcp::optional<ThrowingCopy>>::value);
}

// Performance-related tests
TEST_F(OptionalExtensiveTest, SizeAndAlignment) {
  // Size should be minimal
  EXPECT_LE(sizeof(mcp::optional<char>),
            sizeof(char) + 8);  // Some overhead is ok
  EXPECT_LE(sizeof(mcp::optional<int>), sizeof(int) + 8);

  // Alignment should be preserved
  struct alignas(16) Aligned16 {
    char data[16];
  };
  struct alignas(32) Aligned32 {
    char data[32];
  };

  EXPECT_EQ(alignof(mcp::optional<Aligned16>), 16u);
  EXPECT_EQ(alignof(mcp::optional<Aligned32>), 32u);
}

// Aggregate initialization tests
TEST_F(OptionalExtensiveTest, AggregateTypes) {
  // Direct construction of aggregates
  mcp::optional<Aggregate> opt(mcp::in_place, Aggregate{42, 3.14});
  EXPECT_TRUE(opt);
  EXPECT_EQ(opt->x, 42);
  EXPECT_DOUBLE_EQ(opt->y, 3.14);
}

// Conversion operator tests
TEST_F(OptionalExtensiveTest, ConversionFromOptional) {
  ConvertibleToOptional cto;
  mcp::optional<int> opt = cto;  // Uses user-defined conversion
  EXPECT_TRUE(opt);
  EXPECT_EQ(*opt, 42);
}

// Container tests
TEST_F(OptionalExtensiveTest, InContainers) {
  // optional in vector
  {
    std::vector<mcp::optional<int>> vec;
    vec.push_back(42);
    vec.push_back(mcp::nullopt);
    vec.push_back(99);

    EXPECT_TRUE(vec[0]);
    EXPECT_FALSE(vec[1]);
    EXPECT_TRUE(vec[2]);
    EXPECT_EQ(*vec[0], 42);
    EXPECT_EQ(*vec[2], 99);
  }

  // optional as key in set (requires comparison)
  {
    std::set<mcp::optional<int>> s;
    s.insert(42);
    s.insert(mcp::nullopt);
    s.insert(99);
    s.insert(42);  // Duplicate

    EXPECT_EQ(s.size(), 3u);  // nullopt, 42, 99
    EXPECT_EQ(*s.begin(),
              mcp::nullopt);  // nullopt compares less than any value
  }
}

// Memory management tests
TEST_F(OptionalExtensiveTest, NoLeaks) {
  static int constructions = 0;
  static int destructions = 0;

  struct LeakDetector {
    LeakDetector() { ++constructions; }
    LeakDetector(const LeakDetector&) { ++constructions; }
    LeakDetector(LeakDetector&&) { ++constructions; }
    ~LeakDetector() { ++destructions; }
    LeakDetector& operator=(const LeakDetector&) { return *this; }
    LeakDetector& operator=(LeakDetector&&) { return *this; }
  };

  constructions = destructions = 0;

  {
    mcp::optional<LeakDetector> opt1;
    mcp::optional<LeakDetector> opt2(mcp::in_place);
    mcp::optional<LeakDetector> opt3(opt2);
    opt1 = opt2;
    opt3 = mcp::nullopt;
    opt2 = std::move(opt1);
  }

  EXPECT_EQ(constructions, destructions);
}

// SFINAE tests
TEST_F(OptionalExtensiveTest, SFINAEFriendly) {
  // These should compile
  mcp::optional<int> opt1(42);
  mcp::optional<std::string> opt2("hello");

  // Assignment from different types should work when convertible
  opt1 = static_cast<short>(10);
  opt2 = "world";

  // But not when not convertible
  // opt1 = "string";  // Should not compile
  // opt2 = 42;        // Should not compile
}

// Regression tests for common issues
TEST_F(OptionalExtensiveTest, RegressionTests) {
  // Issue: Self-move should be safe
  {
    mcp::optional<std::string> opt("hello");
    auto& opt_ref = opt;
    opt = std::move(opt_ref);  // Self-move
    EXPECT_TRUE(opt);          // Should still be valid
    // Value is unspecified but valid
  }

  // Issue: Comparison with different optional types
  {
    mcp::optional<int> opt1(42);
    mcp::optional<double> opt2(42.0);
    EXPECT_TRUE(opt1 == opt2);
  }

  // Issue: Reset should work on empty optional
  {
    mcp::optional<int> opt;
    opt.reset();  // Should not crash
    EXPECT_FALSE(opt);
  }
}
