#pragma once

#include <reanimated/CSS/misc/ViewStylesRepository.h>

#include <memory>
#include <string>
#include <type_traits>
#include <typeinfo>

namespace reanimated::css {

using namespace facebook;

enum class RelativeTo : std::uint8_t {
  Parent,
  Self,
};

struct ValueInterpolationContext {
  const std::shared_ptr<const ShadowNode> &node;
  const double fallbackInterpolateThreshold;
};

struct ResolvableValueInterpolationContext {
  const std::shared_ptr<const ShadowNode> &node;
  const double fallbackInterpolateThreshold;
  const std::shared_ptr<ViewStylesRepository> &viewStylesRepository;
  const std::string &relativeProperty;
  const RelativeTo relativeTo;
};

struct CSSValue {
  // This field should be overridden in discrete value types
  static constexpr bool is_discrete_value = false;

  virtual ~CSSValue() = default;

  virtual bool operator==(const CSSValue &other) const = 0;

  virtual folly::dynamic toDynamic() const = 0;
  virtual std::string toString() const = 0;
};

// Base for leaf values that can be interpolated without resolution
template <typename TDerived>
struct CSSSimpleValue : public CSSValue {
  static constexpr bool is_resolvable_value = false;

  bool operator==(const CSSValue &other) const override {
    return typeid(*this) == typeid(other) &&
        *static_cast<const TDerived *>(this) == static_cast<const TDerived &>(other);
  }

  virtual TDerived interpolate(double progress, const TDerived &to) const = 0;
  virtual bool canInterpolateTo(const TDerived &to) const {
    return true;
  }
};

// Base for leaf values that need resolution before interpolation
template <typename TDerived, typename TResolved = TDerived>
struct CSSResolvableValue : public CSSValue {
  static constexpr bool is_resolvable_value = true;

  bool operator==(const CSSValue &other) const override {
    return typeid(*this) == typeid(other) &&
        *static_cast<const TDerived *>(this) == static_cast<const TDerived &>(other);
  }

  virtual TDerived interpolate(double progress, const TDerived &to, const ResolvableValueInterpolationContext &context)
      const = 0;
  virtual std::optional<TResolved> resolve(const ResolvableValueInterpolationContext &context) const {
    return std::nullopt;
  }
  virtual bool canInterpolateTo(const TDerived &to) const {
    return true;
  }
};

// Checks if a type is a resolvable value that needs resolution before
// interpolation
template <typename TCSSValue>
concept Resolvable = requires {
  { TCSSValue::is_resolvable_value } -> std::convertible_to<bool>;
  requires TCSSValue::is_resolvable_value == true;
};

// Checks if a type is a discrete value
template <typename TCSSValue>
concept Discrete = requires {
  { TCSSValue::is_discrete_value } -> std::convertible_to<bool>;
  requires TCSSValue::is_discrete_value == true;
};

// Check if a type is derived from CSSValue
template <typename TCSSValue>
concept CSSValueDerived = std::is_base_of_v<CSSValue, TCSSValue>;

} // namespace reanimated::css
