#pragma once

#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/Types.h>
#include <AK/Utf16String.h>
#include <AK/Variant.h>
#include <AK/Vector.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Value.h>
#include <LibWeb/Bindings/AnimationEffect.h>
#include <LibWeb/Bindings/InterfaceObject.h>

namespace Web::Bindings {

struct KeyframeEffectConstructor {
public:
    static void initialize(JS::Realm&, JS::NativeFunction&);
    static JS::ThrowCompletionOr<GC::Ref<JS::Object>> construct(InterfaceConstructor&, JS::FunctionObject&);

private:
    static JS::ThrowCompletionOr<GC::Ref<JS::Object>> construct0(InterfaceConstructor&, JS::FunctionObject&);
    static JS::ThrowCompletionOr<GC::Ref<JS::Object>> construct1(InterfaceConstructor&, JS::FunctionObject&);
};

struct KeyframeEffectPrototype {
public:
    static void initialize(JS::Realm&, JS::Object&);
    static void define_unforgeable_attributes(JS::Realm&, JS::Object&);

private:
    JS_DECLARE_NATIVE_FUNCTION(target_getter);
    JS_DECLARE_NATIVE_FUNCTION(target_setter);
    JS_DECLARE_NATIVE_FUNCTION(pseudo_element_getter);
    JS_DECLARE_NATIVE_FUNCTION(pseudo_element_setter);
    JS_DECLARE_NATIVE_FUNCTION(composite_getter);
    JS_DECLARE_NATIVE_FUNCTION(composite_setter);
    JS_DECLARE_NATIVE_FUNCTION(get_keyframes);
    JS_DECLARE_NATIVE_FUNCTION(set_keyframes);
};

enum class CompositeOperation : u8 {
    Replace,
    Add,
    Accumulate,
};

JS::ThrowCompletionOr<CompositeOperation> convert_to_idl_value_for_composite_operation(JS::VM&, JS::Value);

Utf16String idl_enum_to_string(CompositeOperation);

enum class CompositeOperationOrAuto : u8 {
    Replace,
    Add,
    Accumulate,
    Auto,
};

JS::ThrowCompletionOr<CompositeOperationOrAuto> convert_to_idl_value_for_composite_operation_or_auto(JS::VM&, JS::Value);

Utf16String idl_enum_to_string(CompositeOperationOrAuto);

struct KeyframeEffectOptions : public EffectTiming {
    CompositeOperation composite { CompositeOperation::Replace };
    Optional<String> pseudo_element { OptionalNone {} };
};

JS::ThrowCompletionOr<KeyframeEffectOptions> convert_to_idl_value_for_keyframe_effect_options(JS::VM&, JS::Value);

struct BasePropertyIndexedKeyframe {
    Variant<CompositeOperationOrAuto, Vector<CompositeOperationOrAuto>> composite { Variant<CompositeOperationOrAuto, Vector<CompositeOperationOrAuto>> { Vector<CompositeOperationOrAuto> {} } };
    Variant<String, Vector<String>> easing { Variant<String, Vector<String>> { Vector<String> {} } };
    Variant<Optional<double>, Vector<Optional<double>>, Empty> offset { Variant<Optional<double>, Vector<Optional<double>>, Empty> { Vector<Optional<double>> {} } };
};

JS::ThrowCompletionOr<BasePropertyIndexedKeyframe> convert_to_idl_value_for_base_property_indexed_keyframe(JS::VM&, JS::Value);

struct BaseKeyframe {
    CompositeOperationOrAuto composite { CompositeOperationOrAuto::Auto };
    String easing { "linear"_string };
    Optional<double> offset { OptionalNone {} };
};

JS::ThrowCompletionOr<BaseKeyframe> convert_to_idl_value_for_base_keyframe(JS::VM&, JS::Value);

} // namespace Web::Bindings
