#include <AK/String.h>
#include <AK/StringView.h>
#include <AK/Variant.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Realm.h>
#include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/TextTrackCue.h>
#include <LibWeb/Bindings/VTTCue.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
#include <LibWeb/WebIDL/Tracing.h>
#include <LibWeb/WebVTT/VTTCue.h>
#include <LibWeb/WebVTT/VTTRegion.h>

namespace Web::Bindings {

void VTTCueConstructor::initialize(JS::Realm& realm, JS::NativeFunction& object)
{
    auto& vm = realm.vm();
    [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable;

    object.set_prototype(&ensure_web_constructor<TextTrackCuePrototype>(realm, "TextTrackCue"_fly_string));
    object.define_direct_property(vm.names.length, JS::Value(3), JS::Attribute::Configurable);
    object.define_direct_property(vm.names.name, JS::PrimitiveString::create(vm, "VTTCue"_utf16), JS::Attribute::Configurable);
    object.define_direct_property(vm.names.prototype, &ensure_web_prototype<VTTCuePrototype>(realm, "VTTCue"_fly_string), 0);
}

JS::ThrowCompletionOr<GC::Ref<JS::Object>> VTTCueConstructor::construct([[maybe_unused]] InterfaceConstructor& constructor, [[maybe_unused]] JS::FunctionObject& new_target)
{
    WebIDL::log_trace(constructor.vm(), "VTTCueConstructor::construct");
    auto& vm = constructor.vm();
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // To internally create a new object implementing the interface VTTCue:

    // 3.2. Let prototype be ? Get(newTarget, "prototype").
    auto prototype = TRY(new_target.get(vm.names.prototype));

    // 3.3. If Type(prototype) is not Object, then:
    if (!prototype.is_object()) {
        // 1. Let targetRealm be ? GetFunctionRealm(newTarget).
        auto* target_realm = TRY(JS::get_function_realm(vm, new_target));

        // 2. Set prototype to the interface prototype object for interface in targetRealm.
        VERIFY(target_realm);
        prototype = &Bindings::ensure_web_prototype<VTTCuePrototype>(*target_realm, "VTTCue"_fly_string);
    }

    if (vm.argument_count() < 3)
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::BadArgCountMany, "VTTCue", "3");

    auto arg0 = vm.argument(0);
    auto start_time = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(arg0.to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "startTime");
        return x;
    }(); }));

    auto arg1 = vm.argument(1);
    auto end_time = TRY(throw_dom_exception_if_needed(vm, [&] { return arg1.to_double(vm); }));

    auto arg2 = vm.argument(2);
    auto text = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, arg2));
    }(); }));

    auto impl = TRY(throw_dom_exception_if_needed(vm, [&] { return WebVTT::VTTCue::construct_impl(realm, start_time, end_time, text); }));

    // 7. Set instance.[[Prototype]] to prototype.
    VERIFY(prototype.is_object());
    impl->set_prototype(&prototype.as_object());

    // FIXME: Steps 8...11. of the "internally create a new object implementing the interface VTTCue" algorithm
    // (https://webidl.spec.whatwg.org/#js-platform-objects) are currently not handled, or are handled within WebVTT::VTTCue::construct_impl().

    return *impl;
}

void VTTCuePrototype::initialize(JS::Realm& realm, JS::Object& object)
{
    [[maybe_unused]] auto& vm = realm.vm();
    [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable | JS::Attribute::Configurable | JS::Attribute::Writable;

    object.set_prototype(GC::Ref { ensure_web_prototype<TextTrackCuePrototype>(realm, "TextTrackCue"_fly_string) });

    auto region_id = "region"_utf16_fly_string;
    auto native_region_getter = JS::NativeFunction::create(realm, region_getter, 0, region_id, &realm, "get"sv);
    auto native_region_setter = JS::NativeFunction::create(realm, region_setter, 1, region_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto region_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(region_id, native_region_getter, native_region_setter, region_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto vertical_id = "vertical"_utf16_fly_string;
    auto native_vertical_getter = JS::NativeFunction::create(realm, vertical_getter, 0, vertical_id, &realm, "get"sv);
    auto native_vertical_setter = JS::NativeFunction::create(realm, vertical_setter, 1, vertical_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto vertical_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(vertical_id, native_vertical_getter, native_vertical_setter, vertical_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto snap_to_lines_id = "snapToLines"_utf16_fly_string;
    auto native_snap_to_lines_getter = JS::NativeFunction::create(realm, snap_to_lines_getter, 0, snap_to_lines_id, &realm, "get"sv);
    auto native_snap_to_lines_setter = JS::NativeFunction::create(realm, snap_to_lines_setter, 1, snap_to_lines_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto snap_to_lines_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(snap_to_lines_id, native_snap_to_lines_getter, native_snap_to_lines_setter, snap_to_lines_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto line_id = "line"_utf16_fly_string;
    auto native_line_getter = JS::NativeFunction::create(realm, line_getter, 0, line_id, &realm, "get"sv);
    auto native_line_setter = JS::NativeFunction::create(realm, line_setter, 1, line_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto line_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(line_id, native_line_getter, native_line_setter, line_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto line_align_id = "lineAlign"_utf16_fly_string;
    auto native_line_align_getter = JS::NativeFunction::create(realm, line_align_getter, 0, line_align_id, &realm, "get"sv);
    auto native_line_align_setter = JS::NativeFunction::create(realm, line_align_setter, 1, line_align_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto line_align_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(line_align_id, native_line_align_getter, native_line_align_setter, line_align_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto position_id = "position"_utf16_fly_string;
    auto native_position_getter = JS::NativeFunction::create(realm, position_getter, 0, position_id, &realm, "get"sv);
    auto native_position_setter = JS::NativeFunction::create(realm, position_setter, 1, position_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto position_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(position_id, native_position_getter, native_position_setter, position_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto position_align_id = "positionAlign"_utf16_fly_string;
    auto native_position_align_getter = JS::NativeFunction::create(realm, position_align_getter, 0, position_align_id, &realm, "get"sv);
    auto native_position_align_setter = JS::NativeFunction::create(realm, position_align_setter, 1, position_align_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto position_align_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(position_align_id, native_position_align_getter, native_position_align_setter, position_align_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto size_id = "size"_utf16_fly_string;
    auto native_size_getter = JS::NativeFunction::create(realm, size_getter, 0, size_id, &realm, "get"sv);
    auto native_size_setter = JS::NativeFunction::create(realm, size_setter, 1, size_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto size_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(size_id, native_size_getter, native_size_setter, size_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto align_id = "align"_utf16_fly_string;
    auto native_align_getter = JS::NativeFunction::create(realm, align_getter, 0, align_id, &realm, "get"sv);
    auto native_align_setter = JS::NativeFunction::create(realm, align_setter, 1, align_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto align_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(align_id, native_align_getter, native_align_setter, align_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto text_id = "text"_utf16_fly_string;
    auto native_text_getter = JS::NativeFunction::create(realm, text_getter, 0, text_id, &realm, "get"sv);
    auto native_text_setter = JS::NativeFunction::create(realm, text_setter, 1, text_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto text_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(text_id, native_text_getter, native_text_setter, text_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    object.define_direct_property("getCueAsHTML"_utf16_fly_string, JS::js_undefined(), default_attributes | JS::Attribute::Unimplemented);

    object.define_direct_property(vm.well_known_symbol_to_string_tag(), JS::PrimitiveString::create(vm, "VTTCue"_utf16), JS::Attribute::Configurable);
}

void VTTCuePrototype::define_unforgeable_attributes(JS::Realm& realm, [[maybe_unused]] JS::Object& object)
{
    [[maybe_unused]] auto& vm = realm.vm();
    [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable;
}

[[maybe_unused]] static JS::ThrowCompletionOr<WebVTT::VTTCue*> impl_from(JS::VM& vm, JS::Value js_value)
{

    if (auto impl = js_value.as_if<WebVTT::VTTCue>())
        return impl.ptr();
    return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "VTTCue");
}

[[maybe_unused]] static JS::ThrowCompletionOr<WebVTT::VTTCue*> impl_from(JS::VM& vm)
{
    auto this_value = vm.this_value();
    if (this_value.is_nullish())
        this_value = &vm.current_realm()->global_object();
    return impl_from(vm, this_value);
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::region_getter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::region_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->region(); }));

    return [&]() -> JS::Value {
        // 1. If the IDL nullable type T? value is null, then the JavaScript value is null.
        if (!R)
            return JS::js_null();

        // 2. Otherwise, the JavaScript value is the result of converting the IDL nullable type value to the inner IDL type T.
        return JS::Value(JS::Value(R));
    }();
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::vertical_getter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::vertical_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->vertical(); }));

    return JS::PrimitiveString::create(vm, idl_enum_to_string(R));
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::snap_to_lines_getter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::snap_to_lines_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->snap_to_lines(); }));

    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::line_getter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::line_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->line(); }));

    return R.visit(
        [&](double const& visited_union_value0) -> JS::Value
        {
            return JS::Value(visited_union_value0);
        },
        [&](AutoKeyword const& visited_union_value1) -> JS::Value
        {
            return JS::PrimitiveString::create(vm, idl_enum_to_string(visited_union_value1));
        }
    );
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::line_align_getter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::line_align_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->line_align(); }));

    return JS::PrimitiveString::create(vm, idl_enum_to_string(R));
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::position_getter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::position_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->position(); }));

    return R.visit(
        [&](double const& visited_union_value0) -> JS::Value
        {
            return JS::Value(visited_union_value0);
        },
        [&](AutoKeyword const& visited_union_value1) -> JS::Value
        {
            return JS::PrimitiveString::create(vm, idl_enum_to_string(visited_union_value1));
        }
    );
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::position_align_getter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::position_align_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->position_align(); }));

    return JS::PrimitiveString::create(vm, idl_enum_to_string(R));
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::size_getter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::size_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->size(); }));

    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::align_getter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::align_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->align(); }));

    return JS::PrimitiveString::create(vm, idl_enum_to_string(R));
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::text_getter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::text_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->text(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::region_setter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::region_setter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] WebVTT::VTTCue* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<GC::Ptr<WebVTT::VTTRegion>> {
        GC::Ptr<WebVTT::VTTRegion> value;

        if (!V.is_nullish()) {

            value = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<WebVTT::VTTRegion>> {
        if (auto impl = V.as_if<WebVTT::VTTRegion>())
            return *impl;
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "VTTRegion");
    }());
        }
        return value;
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_region(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::vertical_setter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::vertical_setter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] WebVTT::VTTCue* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> attribute's type is an enumeration
        // 1. Let S be ? ToString(V).
        auto maybe_idl_value = throw_dom_exception_if_needed(vm, [&] { return convert_to_idl_value_for_direction_setting(vm, V); });

        // 2. If S is not one of the enumeration's values, then return undefined.
        if (maybe_idl_value.is_error())
            return JS::js_undefined();

        // 3. Otherwise, idlValue is the enumeration value equal to S.
        auto idl_value = maybe_idl_value.release_value();

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_vertical(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::snap_to_lines_setter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::snap_to_lines_setter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] WebVTT::VTTCue* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return V.to_boolean(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_snap_to_lines(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::line_setter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::line_setter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] WebVTT::VTTCue* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Variant<double, AutoKeyword>> {

        if (V.is_object()) {
            [[maybe_unused]] auto& object = V.as_object();
        }

        if (V.is_number()) {
            auto line_number = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(V.to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "line");
        return x;
    }());
            return Variant<double, AutoKeyword> { line_number };
        }

        if (V.is_string()) {
            auto enumeration_string = TRY(V.to_utf16_string(vm));
            if (enumeration_string == "auto"sv)
                return Variant<double, AutoKeyword> { AutoKeyword::Auto };
        }


        auto line_number_fallback = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(V.to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "line");
        return x;
    }());
        return Variant<double, AutoKeyword> { line_number_fallback };

        return vm.throw_completion<JS::TypeError>("No union types matched"_utf16);
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_line(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::line_align_setter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::line_align_setter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] WebVTT::VTTCue* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> attribute's type is an enumeration
        // 1. Let S be ? ToString(V).
        auto maybe_idl_value = throw_dom_exception_if_needed(vm, [&] { return convert_to_idl_value_for_line_align_setting(vm, V); });

        // 2. If S is not one of the enumeration's values, then return undefined.
        if (maybe_idl_value.is_error())
            return JS::js_undefined();

        // 3. Otherwise, idlValue is the enumeration value equal to S.
        auto idl_value = maybe_idl_value.release_value();

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_line_align(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::position_setter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::position_setter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] WebVTT::VTTCue* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Variant<double, AutoKeyword>> {

        if (V.is_object()) {
            [[maybe_unused]] auto& object = V.as_object();
        }

        if (V.is_number()) {
            auto position_number = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(V.to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "position");
        return x;
    }());
            return Variant<double, AutoKeyword> { position_number };
        }

        if (V.is_string()) {
            auto enumeration_string = TRY(V.to_utf16_string(vm));
            if (enumeration_string == "auto"sv)
                return Variant<double, AutoKeyword> { AutoKeyword::Auto };
        }


        auto position_number_fallback = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(V.to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "position");
        return x;
    }());
        return Variant<double, AutoKeyword> { position_number_fallback };

        return vm.throw_completion<JS::TypeError>("No union types matched"_utf16);
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_position(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::position_align_setter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::position_align_setter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] WebVTT::VTTCue* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> attribute's type is an enumeration
        // 1. Let S be ? ToString(V).
        auto maybe_idl_value = throw_dom_exception_if_needed(vm, [&] { return convert_to_idl_value_for_position_align_setting(vm, V); });

        // 2. If S is not one of the enumeration's values, then return undefined.
        if (maybe_idl_value.is_error())
            return JS::js_undefined();

        // 3. Otherwise, idlValue is the enumeration value equal to S.
        auto idl_value = maybe_idl_value.release_value();

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_position_align(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::size_setter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::size_setter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] WebVTT::VTTCue* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(V.to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "size");
        return x;
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_size(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::align_setter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::align_setter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] WebVTT::VTTCue* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> attribute's type is an enumeration
        // 1. Let S be ? ToString(V).
        auto maybe_idl_value = throw_dom_exception_if_needed(vm, [&] { return convert_to_idl_value_for_align_setting(vm, V); });

        // 2. If S is not one of the enumeration's values, then return undefined.
        if (maybe_idl_value.is_error())
            return JS::js_undefined();

        // 3. Otherwise, idlValue is the enumeration value equal to S.
        auto idl_value = maybe_idl_value.release_value();

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_align(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

JS_DEFINE_NATIVE_FUNCTION(VTTCuePrototype::text_setter)
{
    WebIDL::log_trace(vm, "VTTCuePrototype::text_setter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] WebVTT::VTTCue* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_text(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

// https://webidl.spec.whatwg.org/#idl-enumeration
JS::ThrowCompletionOr<AutoKeyword> convert_to_idl_value_for_auto_keyword(JS::VM& vm, JS::Value value)
{
    // 1. Let S be the result of calling ? ToString(V).
    auto value_as_string = TRY(value.to_utf16_string(vm));

    // 2. If S is not one of E’s enumeration values, then throw a TypeError.
    // 3. Return the enumeration value of type E that is equal to S.
    if (value_as_string == "auto"sv)
        return AutoKeyword::Auto;
    return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, value_as_string, "AutoKeyword");
}

// https://webidl.spec.whatwg.org/#idl-enumeration
Utf16String idl_enum_to_string(AutoKeyword value)
{
    // The result of converting an IDL enumeration type value to a JavaScript value is the String value that represents the same sequence of code units as the enumeration value.
    switch (value) {
    case AutoKeyword::Auto:
        return "auto"_utf16;
    }
    VERIFY_NOT_REACHED();
}

// https://webidl.spec.whatwg.org/#idl-enumeration
JS::ThrowCompletionOr<DirectionSetting> convert_to_idl_value_for_direction_setting(JS::VM& vm, JS::Value value)
{
    // 1. Let S be the result of calling ? ToString(V).
    auto value_as_string = TRY(value.to_utf16_string(vm));

    // 2. If S is not one of E’s enumeration values, then throw a TypeError.
    // 3. Return the enumeration value of type E that is equal to S.
    if (value_as_string == ""sv)
        return DirectionSetting::Empty;
    if (value_as_string == "rl"sv)
        return DirectionSetting::Rl;
    if (value_as_string == "lr"sv)
        return DirectionSetting::Lr;
    return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, value_as_string, "DirectionSetting");
}

// https://webidl.spec.whatwg.org/#idl-enumeration
Utf16String idl_enum_to_string(DirectionSetting value)
{
    // The result of converting an IDL enumeration type value to a JavaScript value is the String value that represents the same sequence of code units as the enumeration value.
    switch (value) {
    case DirectionSetting::Empty:
        return ""_utf16;
    case DirectionSetting::Rl:
        return "rl"_utf16;
    case DirectionSetting::Lr:
        return "lr"_utf16;
    }
    VERIFY_NOT_REACHED();
}

// https://webidl.spec.whatwg.org/#idl-enumeration
JS::ThrowCompletionOr<LineAlignSetting> convert_to_idl_value_for_line_align_setting(JS::VM& vm, JS::Value value)
{
    // 1. Let S be the result of calling ? ToString(V).
    auto value_as_string = TRY(value.to_utf16_string(vm));

    // 2. If S is not one of E’s enumeration values, then throw a TypeError.
    // 3. Return the enumeration value of type E that is equal to S.
    if (value_as_string == "start"sv)
        return LineAlignSetting::Start;
    if (value_as_string == "center"sv)
        return LineAlignSetting::Center;
    if (value_as_string == "end"sv)
        return LineAlignSetting::End;
    return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, value_as_string, "LineAlignSetting");
}

// https://webidl.spec.whatwg.org/#idl-enumeration
Utf16String idl_enum_to_string(LineAlignSetting value)
{
    // The result of converting an IDL enumeration type value to a JavaScript value is the String value that represents the same sequence of code units as the enumeration value.
    switch (value) {
    case LineAlignSetting::Start:
        return "start"_utf16;
    case LineAlignSetting::Center:
        return "center"_utf16;
    case LineAlignSetting::End:
        return "end"_utf16;
    }
    VERIFY_NOT_REACHED();
}

// https://webidl.spec.whatwg.org/#idl-enumeration
JS::ThrowCompletionOr<PositionAlignSetting> convert_to_idl_value_for_position_align_setting(JS::VM& vm, JS::Value value)
{
    // 1. Let S be the result of calling ? ToString(V).
    auto value_as_string = TRY(value.to_utf16_string(vm));

    // 2. If S is not one of E’s enumeration values, then throw a TypeError.
    // 3. Return the enumeration value of type E that is equal to S.
    if (value_as_string == "line-left"sv)
        return PositionAlignSetting::LineLeft;
    if (value_as_string == "center"sv)
        return PositionAlignSetting::Center;
    if (value_as_string == "line-right"sv)
        return PositionAlignSetting::LineRight;
    if (value_as_string == "auto"sv)
        return PositionAlignSetting::Auto;
    return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, value_as_string, "PositionAlignSetting");
}

// https://webidl.spec.whatwg.org/#idl-enumeration
Utf16String idl_enum_to_string(PositionAlignSetting value)
{
    // The result of converting an IDL enumeration type value to a JavaScript value is the String value that represents the same sequence of code units as the enumeration value.
    switch (value) {
    case PositionAlignSetting::LineLeft:
        return "line-left"_utf16;
    case PositionAlignSetting::Center:
        return "center"_utf16;
    case PositionAlignSetting::LineRight:
        return "line-right"_utf16;
    case PositionAlignSetting::Auto:
        return "auto"_utf16;
    }
    VERIFY_NOT_REACHED();
}

// https://webidl.spec.whatwg.org/#idl-enumeration
JS::ThrowCompletionOr<AlignSetting> convert_to_idl_value_for_align_setting(JS::VM& vm, JS::Value value)
{
    // 1. Let S be the result of calling ? ToString(V).
    auto value_as_string = TRY(value.to_utf16_string(vm));

    // 2. If S is not one of E’s enumeration values, then throw a TypeError.
    // 3. Return the enumeration value of type E that is equal to S.
    if (value_as_string == "start"sv)
        return AlignSetting::Start;
    if (value_as_string == "center"sv)
        return AlignSetting::Center;
    if (value_as_string == "end"sv)
        return AlignSetting::End;
    if (value_as_string == "left"sv)
        return AlignSetting::Left;
    if (value_as_string == "right"sv)
        return AlignSetting::Right;
    return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, value_as_string, "AlignSetting");
}

// https://webidl.spec.whatwg.org/#idl-enumeration
Utf16String idl_enum_to_string(AlignSetting value)
{
    // The result of converting an IDL enumeration type value to a JavaScript value is the String value that represents the same sequence of code units as the enumeration value.
    switch (value) {
    case AlignSetting::Start:
        return "start"_utf16;
    case AlignSetting::Center:
        return "center"_utf16;
    case AlignSetting::End:
        return "end"_utf16;
    case AlignSetting::Left:
        return "left"_utf16;
    case AlignSetting::Right:
        return "right"_utf16;
    }
    VERIFY_NOT_REACHED();
}

} // namespace Web::Bindings
