#pragma once

#include <algorithm>
#include <atomic>
#include <cstdint>
#include <cstdio>
#include <cstdlib>

/**
 * @brief High-performance thread-safe reference counting system
 *
 * This library provides custom smart pointers with shared/weak semantics,
 * designed for better performance than std::shared_ptr/weak_ptr.
 *
 * Key features:
 * - Thread-safe reference counting using atomic operations
 * - Weak references to break circular dependencies
 * - Single allocation for better cache locality
 * - Optimized for copy/move operations
 * - Compatible with std::shared_ptr semantics
 *
 * Basic usage:
 * @code
 * auto obj = make_ref<MyClass>(args...);     // Create managed object
 * auto copy = obj.copy();                    // Explicit copy (thread-safe)
 * WeakRef<MyClass> weak = obj.as_weak();     // Create weak reference
 * auto locked = weak.lock();                 // Try to promote to strong
 * @endcode
 *
 * Thread safety: All operations are thread-safe. Multiple threads can
 * safely copy, move, and destroy references to the same object.
 */

// Forward declaration
template <typename T> struct WeakRef;

namespace detail {
struct ControlBlock {
  std::atomic<uint32_t> strong_count;
  std::atomic<uint32_t> weak_count;

  ControlBlock()
      : strong_count(1), weak_count(1) {
  } // Start with 1 strong, 1 weak (biased)

  /**
   * @brief Increment strong reference count
   * @return Previous strong count
   */
  uint32_t increment_strong() noexcept {
    return strong_count.fetch_add(1, std::memory_order_relaxed);
  }

  /**
   * @brief Decrement strong reference count
   * @return Previous strong count
   */
  uint32_t decrement_strong() noexcept {
    return strong_count.fetch_sub(1, std::memory_order_acq_rel);
  }

  /**
   * @brief Increment weak reference count
   * @return Previous weak count
   */
  uint32_t increment_weak() noexcept {
    return weak_count.fetch_add(1, std::memory_order_relaxed);
  }

  /**
   * @brief Decrement weak reference count
   * @return Previous weak count
   */
  uint32_t decrement_weak() noexcept {
    return weak_count.fetch_sub(1, std::memory_order_acq_rel);
  }
};
} // namespace detail

/**
 * @brief Strong reference to a shared object (similar to std::shared_ptr)
 *
 * Ref<T> manages shared ownership of an object. The object is automatically
 * destroyed when the last Ref pointing to it is destroyed.
 *
 * Usage:
 * - Use make_ref<T>() to create new objects
 * - Use copy() method for explicit sharing of ownership
 * - Use get(), operator*, operator-> to access the object
 * - Use operator bool() to check if valid
 * - Use reset() to release ownership
 *
 * Limitations compared to std::shared_ptr:
 * - Cannot take ownership of raw pointers
 * - Objects can only be created via make_ref<T>() for proper memory layout
 * - No custom deleter support
 * - No enable_shared_from_this / shared_from_this() integration
 * - No aliasing constructor (sharing ownership with different pointer)
 * - No array support (Ref<T[]>)
 * - No atomic operations (atomic_load, atomic_store, etc.)
 *
 * Thread safety: All operations are thread-safe. The managed object
 * itself is NOT automatically thread-safe.
 */
template <typename T> struct Ref {
  /**
   * @brief Get raw pointer to managed object
   */
  T *get() const noexcept { return ptr; }

  /**
   * @brief Dereference operator
   */
  T &operator*() const { return *ptr; }

  /**
   * @brief Arrow operator
   */
  T *operator->() const { return ptr; }

  /**
   * @brief Check if Ref is valid (not empty)
   */
  explicit operator bool() const noexcept { return ptr != nullptr; }

  /**
   * @brief Destructor - decrements strong reference count
   */
  ~Ref() { release(); }

  /**
   * @brief Copy constructor - deleted to prevent accidental copies
   * Use copy() method for explicit copying
   */
  Ref(const Ref &other) = delete;

  /**
   * @brief Converting copy constructor - deleted to prevent accidental copies
   * Use copy() method for explicit copying
   */
  template <typename U> Ref(const Ref<U> &other) = delete;

  /**
   * @brief Copy assignment operator - deleted to prevent accidental copies
   * Use copy() method for explicit copying
   */
  Ref &operator=(const Ref &other) = delete;

  /**
   * @brief Converting assignment operator - deleted to prevent accidental
   * copies Use copy() method for explicit copying
   */
  template <typename U> Ref &operator=(const Ref<U> &other) = delete;

  /**
   * @brief Move constructor - transfers ownership
   */
  Ref(Ref &&other) noexcept
      : ptr(other.ptr), control_block(other.control_block) {
    other.ptr = nullptr;
    other.control_block = nullptr;
  }

  /**
   * @brief Converting move constructor for polymorphism (Derived -> Base)
   */
  template <typename U>
  Ref(Ref<U> &&other) noexcept
    requires std::is_convertible_v<U *, T *>
      : ptr(other.ptr), control_block(other.control_block) {
    other.ptr = nullptr;
    other.control_block = nullptr;
  }

  /**
   * @brief Move assignment operator
   */
  Ref &operator=(Ref &&other) noexcept {
    if (this != &other) {
      release();
      ptr = other.ptr;
      control_block = other.control_block;
      other.ptr = nullptr;
      other.control_block = nullptr;
    }
    return *this;
  }

  /**
   * @brief Converting move assignment operator for polymorphism (Derived ->
   * Base)
   */
  template <typename U>
  Ref &operator=(Ref<U> &&other) noexcept
    requires std::is_convertible_v<U *, T *>
  {
    release();
    ptr = other.ptr;
    control_block = other.control_block;
    other.ptr = nullptr;
    other.control_block = nullptr;
    return *this;
  }

  /**
   * @brief Explicitly create a copy with shared ownership
   * @return New Ref that shares ownership of the same object
   */
  [[nodiscard]] Ref copy() const noexcept {
    if (control_block) {
      control_block->increment_strong();
    }
    return Ref(ptr, control_block);
  }

  /**
   * @brief Create a WeakRef that observes this object
   * @return New WeakRef that observes the same object
   */
  [[nodiscard]] WeakRef<T> as_weak() const noexcept {
    if (control_block) {
      control_block->increment_weak();
    }
    return WeakRef<T>(ptr, control_block);
  }

  /**
   * @brief Reset to empty state
   */
  void reset() noexcept {
    release();
    ptr = nullptr;
    control_block = nullptr;
  }

  /**
   * @brief Equality comparison
   */
  bool operator==(const Ref &other) const noexcept {
    return control_block == other.control_block;
  }

  /**
   * @brief Inequality comparison
   */
  bool operator!=(const Ref &other) const noexcept { return !(*this == other); }

  /**
   * @brief Default constructor - creates empty Ref
   */
  Ref() : ptr(nullptr), control_block(nullptr) {}

private:
  explicit Ref(T *object_ptr, detail::ControlBlock *cb)
      : ptr(object_ptr), control_block(cb) {}

  T *ptr;
  detail::ControlBlock *control_block;

  /**
   * @brief Release current reference and handle cleanup
   */
  void release() noexcept {
    if (control_block) {
      uint32_t prev_strong = control_block->decrement_strong();

      // If this was the last strong reference, destroy the object
      if (prev_strong == 1) {
        // We need to call the destructor before we decrement the weak count, to
        // account for the possibility that T has a WeakRef to itself.
        ptr->~T();

        // Release the bias - decrement weak count for strong references
        uint32_t prev_weak = control_block->decrement_weak();

        // If weak count hits 0, destroy and free control block
        if (prev_weak == 1) {
          control_block->~ControlBlock();
          std::free(control_block);
        }
      }
    }
  }

  template <typename U, typename... Args>
  friend Ref<U> make_ref(Args &&...args);

  template <typename U> friend struct WeakRef;
  template <typename U> friend struct Ref;
};

/**
 * @brief Weak reference to a shared object (similar to std::weak_ptr)
 *
 * WeakRef<T> holds a non-owning reference to an object managed by Ref<T>.
 * It can be used to break circular dependencies and safely observe objects
 * that might be destroyed by other threads.
 *
 * Usage:
 * - Create from Ref<T> using as_weak() to observe without owning
 * - Use copy() method for explicit copying
 * - Use lock() to attempt promotion to Ref<T>
 * - Returns empty Ref<T> if object was already destroyed
 * - Use reset() to stop observing
 *
 * Self-referencing pattern: Objects can safely contain WeakRef members
 * pointing to themselves. The implementation ensures proper destruction
 * order to prevent use-after-free when the object destructor runs.
 *
 * Thread safety: All operations are thread-safe. The observed object
 * may be destroyed by other threads at any time.
 */
template <typename T> struct WeakRef {
  /**
   * @brief Attempt to promote WeakRef to Ref
   * @return Valid Ref if object still alive, empty Ref otherwise
   */
  Ref<T> lock() const {
    if (!control_block) {
      return Ref<T>();
    }

    // Try to increment strong count if it's not zero
    uint32_t expected_strong =
        control_block->strong_count.load(std::memory_order_relaxed);
    while (expected_strong > 0) {
      // Try to increment the strong count
      if (control_block->strong_count.compare_exchange_weak(
              expected_strong, expected_strong + 1, std::memory_order_acquire,
              std::memory_order_relaxed)) {
        // Success - we incremented the strong count
        return Ref<T>(ptr, control_block);
      }
      // CAS failed, expected_strong now contains the current value, retry
    }

    // Strong count was 0, object is being destroyed
    return Ref<T>();
  }

  /**
   * @brief Destructor - decrements weak reference count
   */
  ~WeakRef() { release(); }

  /**
   * @brief Copy constructor from WeakRef - deleted to prevent accidental copies
   * Use copy() method for explicit copying
   */
  WeakRef(const WeakRef &other) = delete;

  /**
   * @brief Copy constructor from Ref - deleted to prevent accidental copies
   * Use copy() method for explicit copying
   */
  WeakRef(const Ref<T> &ref) = delete;

  /**
   * @brief Converting copy constructor from WeakRef - deleted to prevent
   * accidental copies Use copy() method for explicit copying
   */
  template <typename U> WeakRef(const WeakRef<U> &other) = delete;

  /**
   * @brief Converting copy constructor from Ref - deleted to prevent accidental
   * copies Use copy() method for explicit copying
   */
  template <typename U> WeakRef(const Ref<U> &ref) = delete;

  /**
   * @brief Converting copy assignment from WeakRef - deleted to prevent
   * accidental copies Use copy() method for explicit copying
   */
  template <typename U> WeakRef &operator=(const WeakRef<U> &other) = delete;

  /**
   * @brief Converting copy assignment from Ref - deleted to prevent accidental
   * copies Use copy() method for explicit copying
   */
  template <typename U> WeakRef &operator=(const Ref<U> &ref) = delete;

  /**
   * @brief Converting move constructor from WeakRef for polymorphism
   */
  template <typename U>
  WeakRef(WeakRef<U> &&other) noexcept
    requires std::is_convertible_v<U *, T *>
      : ptr(other.ptr), control_block(other.control_block) {
    other.ptr = nullptr;
    other.control_block = nullptr;
  }

  /**
   * @brief Converting move assignment from WeakRef for polymorphism
   */
  template <typename U>
  WeakRef &operator=(WeakRef<U> &&other) noexcept
    requires std::is_convertible_v<U *, T *>
  {
    release();
    ptr = other.ptr;
    control_block = other.control_block;
    other.ptr = nullptr;
    other.control_block = nullptr;
    return *this;
  }

  /**
   * @brief Copy assignment from WeakRef - deleted to prevent accidental copies
   * Use copy() method for explicit copying
   */
  WeakRef &operator=(const WeakRef &other) = delete;

  /**
   * @brief Copy assignment from Ref - deleted to prevent accidental copies
   * Use copy() method for explicit copying
   */
  WeakRef &operator=(const Ref<T> &ref) = delete;

  /**
   * @brief Move constructor
   */
  WeakRef(WeakRef &&other) noexcept
      : ptr(other.ptr), control_block(other.control_block) {
    other.ptr = nullptr;
    other.control_block = nullptr;
  }

  /**
   * @brief Move assignment
   */
  WeakRef &operator=(WeakRef &&other) noexcept {
    if (this != &other) {
      release();
      ptr = other.ptr;
      control_block = other.control_block;
      other.ptr = nullptr;
      other.control_block = nullptr;
    }
    return *this;
  }

  /**
   * @brief Explicitly create a copy with shared weak reference
   * @return New WeakRef that observes the same object
   */
  [[nodiscard]] WeakRef copy() const noexcept {
    if (control_block) {
      control_block->increment_weak();
    }
    return WeakRef(ptr, control_block);
  }

  /**
   * @brief Reset to empty state
   */
  void reset() noexcept {
    release();
    ptr = nullptr;
    control_block = nullptr;
  }

  /**
   * @brief Default constructor - creates empty WeakRef
   */
  WeakRef() : ptr(nullptr), control_block(nullptr) {}

private:
  explicit WeakRef(T *object_ptr, detail::ControlBlock *cb)
      : ptr(object_ptr), control_block(cb) {}

  T *ptr;
  detail::ControlBlock *control_block;

  /**
   * @brief Release current weak reference and handle cleanup
   */
  void release() noexcept {
    if (control_block) {
      uint32_t prev_weak = control_block->decrement_weak();

      // If weak count hits 0, destroy and free control block
      if (prev_weak == 1) {
        control_block->~ControlBlock();
        std::free(control_block);
      }
    }
  }

  template <typename U> friend struct Ref;
  template <typename U> friend struct WeakRef;
};

/**
 * @brief Create a new managed object wrapped in Ref<T>
 *
 * This is the only way to create Ref<T> objects. It performs a single
 * allocation for both the control block and object, improving cache locality.
 *
 * @tparam T Type of object to create
 * @tparam Args Types of constructor arguments
 * @param args Arguments forwarded to T's constructor
 * @return Ref<T> managing the newly created object
 *
 * Example:
 * @code
 * auto obj = make_ref<MyClass>(arg1, arg2);
 * auto empty_vec = make_ref<std::vector<int>>();
 * auto obj_copy = obj.copy();                    // Explicit copy
 * WeakRef<MyClass> weak = obj.as_weak();         // Create weak reference
 * @endcode
 *
 * Thread safety: Safe to call from multiple threads simultaneously.
 */
template <typename T, typename... Args> Ref<T> make_ref(Args &&...args) {
  constexpr size_t cb_size = sizeof(detail::ControlBlock);
  constexpr size_t alignment = alignof(T);
  constexpr size_t padded_cb_size =
      (cb_size + alignment - 1) & ~(alignment - 1);

  constexpr size_t total_alignment =
      std::max(alignof(detail::ControlBlock), alignment);
  constexpr size_t total_size = padded_cb_size + sizeof(T);
  constexpr size_t aligned_total_size =
      (total_size + total_alignment - 1) & ~(total_alignment - 1);

  char *buf = reinterpret_cast<char *>(
      std::aligned_alloc(total_alignment, aligned_total_size));
  if (!buf) {
    std::fprintf(stderr, "Out of memory\n");
    std::abort();
  }

  auto *cb = new (buf) detail::ControlBlock();
  T *obj = new (buf + padded_cb_size) T{std::forward<Args>(args)...};
  return Ref<T>(obj, cb);
}
