#include <AK/String.h>
#include <AK/TypeCasts.h>
#include <AK/Utf16String.h>
#include <AK/Variant.h>
#include <LibGC/ConservativeVector.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibWeb/Bindings/CSSNumericValue.h>
#include <LibWeb/Bindings/CSSStyleValue.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/CSS/CSSNumericValue.h>
#include <LibWeb/CSS/CSSUnitValue.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
#include <LibWeb/WebIDL/Tracing.h>
#include <LibWeb/WebIDL/Types.h>

namespace Web::Bindings {

void CSSNumericValueConstructor::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<CSSStyleValuePrototype>(realm, "CSSStyleValue"_fly_string));
    object.define_direct_property(vm.names.length, JS::Value(0), JS::Attribute::Configurable);
    object.define_direct_property(vm.names.name, JS::PrimitiveString::create(vm, "CSSNumericValue"_utf16), JS::Attribute::Configurable);
    object.define_direct_property(vm.names.prototype, &ensure_web_prototype<CSSNumericValuePrototype>(realm, "CSSNumericValue"_fly_string), 0);
    if (is<HTML::Window>(realm.global_object())) {
        object.define_native_function(realm, "parse"_utf16_fly_string, parse, 1, JS::Attribute::Enumerable | JS::Attribute::Configurable | JS::Attribute::Writable);
    
        }
}

JS::ThrowCompletionOr<GC::Ref<JS::Object>> CSSNumericValueConstructor::construct([[maybe_unused]] InterfaceConstructor& constructor, [[maybe_unused]] JS::FunctionObject& new_target)
{
    WebIDL::log_trace(constructor.vm(), "CSSNumericValueConstructor::construct");
    return constructor.vm().throw_completion<JS::TypeError>(JS::ErrorType::NotAConstructor, "CSSNumericValue");
}

void CSSNumericValuePrototype::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<CSSStyleValuePrototype>(realm, "CSSStyleValue"_fly_string) });
    object.define_native_function(realm, "add"_utf16_fly_string, add, 0, default_attributes);

    object.define_native_function(realm, "sub"_utf16_fly_string, sub, 0, default_attributes);

    object.define_native_function(realm, "mul"_utf16_fly_string, mul, 0, default_attributes);

    object.define_native_function(realm, "div"_utf16_fly_string, div, 0, default_attributes);

    object.define_native_function(realm, "min"_utf16_fly_string, min, 0, default_attributes);

    object.define_native_function(realm, "max"_utf16_fly_string, max, 0, default_attributes);

    object.define_native_function(realm, "equals"_utf16_fly_string, equals, 0, default_attributes);

    object.define_native_function(realm, "to"_utf16_fly_string, to, 1, default_attributes);

    object.define_native_function(realm, "type"_utf16_fly_string, type, 0, default_attributes);

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

void CSSNumericValuePrototype::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<CSS::CSSNumericValue*> impl_from(JS::VM& vm, JS::Value js_value)
{

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

[[maybe_unused]] static JS::ThrowCompletionOr<CSS::CSSNumericValue*> 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(CSSNumericValueConstructor::parse)
{
    WebIDL::log_trace(vm, "CSSNumericValueConstructor::parse");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    if (vm.argument_count() < 1)
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::BadArgCountOne, "parse");

    auto arg0 = vm.argument(0);
    auto css_text = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_usv_string(vm, arg0));
    }(); }));

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return CSS::CSSNumericValue::parse(vm, css_text); }));
    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(CSSNumericValuePrototype::add)
{
    WebIDL::log_trace(vm, "CSSNumericValuePrototype::add");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] CSS::CSSNumericValue* idl_object = TRY(impl_from(vm));

    GC::ConservativeVector<Variant<double, GC::Ref<CSS::CSSNumericValue>>> values;
    if (vm.argument_count() > 0) {
        values.ensure_capacity(vm.argument_count() - 0);
        for (size_t i = 0; i < vm.argument_count(); ++i) {
            auto argument = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Variant<double, GC::Ref<CSS::CSSNumericValue>>> {

        if (vm.argument(i).is_object()) {
            [[maybe_unused]] auto& object = vm.argument(i).as_object();

            if (is<PlatformObject>(object)) {

                if (auto* result = as_if<CSS::CSSNumericValue>(object))
                    return Variant<double, GC::Ref<CSS::CSSNumericValue>> { GC::Ref { *result } };
            }
        }

        if (vm.argument(i).is_number()) {
            auto values_number = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
            return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number };
        }

        auto values_number_fallback = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
        return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number_fallback };

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

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->add(values); }));
    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(CSSNumericValuePrototype::sub)
{
    WebIDL::log_trace(vm, "CSSNumericValuePrototype::sub");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] CSS::CSSNumericValue* idl_object = TRY(impl_from(vm));

    GC::ConservativeVector<Variant<double, GC::Ref<CSS::CSSNumericValue>>> values;
    if (vm.argument_count() > 0) {
        values.ensure_capacity(vm.argument_count() - 0);
        for (size_t i = 0; i < vm.argument_count(); ++i) {
            auto argument = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Variant<double, GC::Ref<CSS::CSSNumericValue>>> {

        if (vm.argument(i).is_object()) {
            [[maybe_unused]] auto& object = vm.argument(i).as_object();

            if (is<PlatformObject>(object)) {

                if (auto* result = as_if<CSS::CSSNumericValue>(object))
                    return Variant<double, GC::Ref<CSS::CSSNumericValue>> { GC::Ref { *result } };
            }
        }

        if (vm.argument(i).is_number()) {
            auto values_number = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
            return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number };
        }

        auto values_number_fallback = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
        return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number_fallback };

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

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->sub(values); }));
    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(CSSNumericValuePrototype::mul)
{
    WebIDL::log_trace(vm, "CSSNumericValuePrototype::mul");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] CSS::CSSNumericValue* idl_object = TRY(impl_from(vm));

    GC::ConservativeVector<Variant<double, GC::Ref<CSS::CSSNumericValue>>> values;
    if (vm.argument_count() > 0) {
        values.ensure_capacity(vm.argument_count() - 0);
        for (size_t i = 0; i < vm.argument_count(); ++i) {
            auto argument = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Variant<double, GC::Ref<CSS::CSSNumericValue>>> {

        if (vm.argument(i).is_object()) {
            [[maybe_unused]] auto& object = vm.argument(i).as_object();

            if (is<PlatformObject>(object)) {

                if (auto* result = as_if<CSS::CSSNumericValue>(object))
                    return Variant<double, GC::Ref<CSS::CSSNumericValue>> { GC::Ref { *result } };
            }
        }

        if (vm.argument(i).is_number()) {
            auto values_number = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
            return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number };
        }

        auto values_number_fallback = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
        return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number_fallback };

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

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->mul(values); }));
    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(CSSNumericValuePrototype::div)
{
    WebIDL::log_trace(vm, "CSSNumericValuePrototype::div");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] CSS::CSSNumericValue* idl_object = TRY(impl_from(vm));

    GC::ConservativeVector<Variant<double, GC::Ref<CSS::CSSNumericValue>>> values;
    if (vm.argument_count() > 0) {
        values.ensure_capacity(vm.argument_count() - 0);
        for (size_t i = 0; i < vm.argument_count(); ++i) {
            auto argument = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Variant<double, GC::Ref<CSS::CSSNumericValue>>> {

        if (vm.argument(i).is_object()) {
            [[maybe_unused]] auto& object = vm.argument(i).as_object();

            if (is<PlatformObject>(object)) {

                if (auto* result = as_if<CSS::CSSNumericValue>(object))
                    return Variant<double, GC::Ref<CSS::CSSNumericValue>> { GC::Ref { *result } };
            }
        }

        if (vm.argument(i).is_number()) {
            auto values_number = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
            return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number };
        }

        auto values_number_fallback = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
        return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number_fallback };

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

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->div(values); }));
    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(CSSNumericValuePrototype::min)
{
    WebIDL::log_trace(vm, "CSSNumericValuePrototype::min");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] CSS::CSSNumericValue* idl_object = TRY(impl_from(vm));

    GC::ConservativeVector<Variant<double, GC::Ref<CSS::CSSNumericValue>>> values;
    if (vm.argument_count() > 0) {
        values.ensure_capacity(vm.argument_count() - 0);
        for (size_t i = 0; i < vm.argument_count(); ++i) {
            auto argument = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Variant<double, GC::Ref<CSS::CSSNumericValue>>> {

        if (vm.argument(i).is_object()) {
            [[maybe_unused]] auto& object = vm.argument(i).as_object();

            if (is<PlatformObject>(object)) {

                if (auto* result = as_if<CSS::CSSNumericValue>(object))
                    return Variant<double, GC::Ref<CSS::CSSNumericValue>> { GC::Ref { *result } };
            }
        }

        if (vm.argument(i).is_number()) {
            auto values_number = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
            return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number };
        }

        auto values_number_fallback = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
        return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number_fallback };

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

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->min(values); }));
    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(CSSNumericValuePrototype::max)
{
    WebIDL::log_trace(vm, "CSSNumericValuePrototype::max");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] CSS::CSSNumericValue* idl_object = TRY(impl_from(vm));

    GC::ConservativeVector<Variant<double, GC::Ref<CSS::CSSNumericValue>>> values;
    if (vm.argument_count() > 0) {
        values.ensure_capacity(vm.argument_count() - 0);
        for (size_t i = 0; i < vm.argument_count(); ++i) {
            auto argument = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Variant<double, GC::Ref<CSS::CSSNumericValue>>> {

        if (vm.argument(i).is_object()) {
            [[maybe_unused]] auto& object = vm.argument(i).as_object();

            if (is<PlatformObject>(object)) {

                if (auto* result = as_if<CSS::CSSNumericValue>(object))
                    return Variant<double, GC::Ref<CSS::CSSNumericValue>> { GC::Ref { *result } };
            }
        }

        if (vm.argument(i).is_number()) {
            auto values_number = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
            return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number };
        }

        auto values_number_fallback = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "values");
        return x;
    }());
        return Variant<double, GC::Ref<CSS::CSSNumericValue>> { values_number_fallback };

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

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->max(values); }));
    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(CSSNumericValuePrototype::equals)
{
    WebIDL::log_trace(vm, "CSSNumericValuePrototype::equals");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] CSS::CSSNumericValue* idl_object = TRY(impl_from(vm));

    GC::ConservativeVector<Variant<double, GC::Ref<CSS::CSSNumericValue>>> value;
    if (vm.argument_count() > 0) {
        value.ensure_capacity(vm.argument_count() - 0);
        for (size_t i = 0; i < vm.argument_count(); ++i) {
            auto argument = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Variant<double, GC::Ref<CSS::CSSNumericValue>>> {

        if (vm.argument(i).is_object()) {
            [[maybe_unused]] auto& object = vm.argument(i).as_object();

            if (is<PlatformObject>(object)) {

                if (auto* result = as_if<CSS::CSSNumericValue>(object))
                    return Variant<double, GC::Ref<CSS::CSSNumericValue>> { GC::Ref { *result } };
            }
        }

        if (vm.argument(i).is_number()) {
            auto value_number = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "value");
        return x;
    }());
            return Variant<double, GC::Ref<CSS::CSSNumericValue>> { value_number };
        }

        auto value_number_fallback = TRY([&]() -> JS::ThrowCompletionOr<double> {
        auto x = TRY(vm.argument(i).to_double(vm));
        if (isinf(x) || isnan(x))
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "value");
        return x;
    }());
        return Variant<double, GC::Ref<CSS::CSSNumericValue>> { value_number_fallback };

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

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->equals_for_bindings(value); }));
    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(CSSNumericValuePrototype::to)
{
    WebIDL::log_trace(vm, "CSSNumericValuePrototype::to");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] CSS::CSSNumericValue* idl_object = TRY(impl_from(vm));

    if (vm.argument_count() < 1)
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::BadArgCountOne, "to");

    auto arg0 = vm.argument(0);
    auto unit = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Utf16String> {
        return TRY(WebIDL::to_utf16_usv_string(vm, arg0));
    }(); }));

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->to(unit); }));
    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(CSSNumericValuePrototype::type)
{
    WebIDL::log_trace(vm, "CSSNumericValuePrototype::type");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] CSS::CSSNumericValue* idl_object = TRY(impl_from(vm));

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->type_for_bindings(); }));
    return [&]() -> JS::Value {
        // 1. Let O be OrdinaryObjectCreate(%Object.prototype%).
        auto dictionary_object = JS::Object::create(realm, realm.intrinsics().object_prototype());

        // 1. Let key be the identifier of member.
        // 2. If V[key] exists, then:
        if (R.angle.has_value()) {

        // 1. Let idlValue be V[key].
        // 2. Let value be the result of converting idlValue to a JavaScript value.
        // 3. Perform ! CreateDataPropertyOrThrow(O, key, value).
        MUST(dictionary_object->create_data_property("angle"_utf16_fly_string, JS::Value(static_cast<WebIDL::Long>(R.angle.value()))));
        }

        // 1. Let key be the identifier of member.
        // 2. If V[key] exists, then:
        if (R.flex.has_value()) {

        // 1. Let idlValue be V[key].
        // 2. Let value be the result of converting idlValue to a JavaScript value.
        // 3. Perform ! CreateDataPropertyOrThrow(O, key, value).
        MUST(dictionary_object->create_data_property("flex"_utf16_fly_string, JS::Value(static_cast<WebIDL::Long>(R.flex.value()))));
        }

        // 1. Let key be the identifier of member.
        // 2. If V[key] exists, then:
        if (R.frequency.has_value()) {

        // 1. Let idlValue be V[key].
        // 2. Let value be the result of converting idlValue to a JavaScript value.
        // 3. Perform ! CreateDataPropertyOrThrow(O, key, value).
        MUST(dictionary_object->create_data_property("frequency"_utf16_fly_string, JS::Value(static_cast<WebIDL::Long>(R.frequency.value()))));
        }

        // 1. Let key be the identifier of member.
        // 2. If V[key] exists, then:
        if (R.length.has_value()) {

        // 1. Let idlValue be V[key].
        // 2. Let value be the result of converting idlValue to a JavaScript value.
        // 3. Perform ! CreateDataPropertyOrThrow(O, key, value).
        MUST(dictionary_object->create_data_property("length"_utf16_fly_string, JS::Value(static_cast<WebIDL::Long>(R.length.value()))));
        }

        // 1. Let key be the identifier of member.
        // 2. If V[key] exists, then:
        if (R.percent.has_value()) {

        // 1. Let idlValue be V[key].
        // 2. Let value be the result of converting idlValue to a JavaScript value.
        // 3. Perform ! CreateDataPropertyOrThrow(O, key, value).
        MUST(dictionary_object->create_data_property("percent"_utf16_fly_string, JS::Value(static_cast<WebIDL::Long>(R.percent.value()))));
        }

        // 1. Let key be the identifier of member.
        // 2. If V[key] exists, then:
        if (R.percent_hint.has_value()) {

        // 1. Let idlValue be V[key].
        // 2. Let value be the result of converting idlValue to a JavaScript value.
        // 3. Perform ! CreateDataPropertyOrThrow(O, key, value).
        MUST(dictionary_object->create_data_property("percentHint"_utf16_fly_string, JS::PrimitiveString::create(vm, idl_enum_to_string(R.percent_hint.value()))));
        }

        // 1. Let key be the identifier of member.
        // 2. If V[key] exists, then:
        if (R.resolution.has_value()) {

        // 1. Let idlValue be V[key].
        // 2. Let value be the result of converting idlValue to a JavaScript value.
        // 3. Perform ! CreateDataPropertyOrThrow(O, key, value).
        MUST(dictionary_object->create_data_property("resolution"_utf16_fly_string, JS::Value(static_cast<WebIDL::Long>(R.resolution.value()))));
        }

        // 1. Let key be the identifier of member.
        // 2. If V[key] exists, then:
        if (R.time.has_value()) {

        // 1. Let idlValue be V[key].
        // 2. Let value be the result of converting idlValue to a JavaScript value.
        // 3. Perform ! CreateDataPropertyOrThrow(O, key, value).
        MUST(dictionary_object->create_data_property("time"_utf16_fly_string, JS::Value(static_cast<WebIDL::Long>(R.time.value()))));
        }

        // 4. Return O.
        return dictionary_object;
    }();
}

// https://webidl.spec.whatwg.org/#idl-enumeration
JS::ThrowCompletionOr<CSSNumericBaseType> convert_to_idl_value_for_css_numeric_base_type(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 == "length"sv)
        return CSSNumericBaseType::Length;
    if (value_as_string == "angle"sv)
        return CSSNumericBaseType::Angle;
    if (value_as_string == "time"sv)
        return CSSNumericBaseType::Time;
    if (value_as_string == "frequency"sv)
        return CSSNumericBaseType::Frequency;
    if (value_as_string == "resolution"sv)
        return CSSNumericBaseType::Resolution;
    if (value_as_string == "flex"sv)
        return CSSNumericBaseType::Flex;
    if (value_as_string == "percent"sv)
        return CSSNumericBaseType::Percent;
    return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, value_as_string, "CSSNumericBaseType");
}

// https://webidl.spec.whatwg.org/#idl-enumeration
Utf16String idl_enum_to_string(CSSNumericBaseType 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 CSSNumericBaseType::Length:
        return "length"_utf16;
    case CSSNumericBaseType::Angle:
        return "angle"_utf16;
    case CSSNumericBaseType::Time:
        return "time"_utf16;
    case CSSNumericBaseType::Frequency:
        return "frequency"_utf16;
    case CSSNumericBaseType::Resolution:
        return "resolution"_utf16;
    case CSSNumericBaseType::Flex:
        return "flex"_utf16;
    case CSSNumericBaseType::Percent:
        return "percent"_utf16;
    }
    VERIFY_NOT_REACHED();
}

// https://webidl.spec.whatwg.org/#es-dictionary
JS::ThrowCompletionOr<CSSNumericType> convert_to_idl_value_for_css_numeric_type(JS::VM& vm, JS::Value js_dict)
{
    // 1. If jsDict is not an Object and jsDict is neither undefined nor null, then throw a TypeError.
    if (!js_dict.is_object() && !js_dict.is_nullish())
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "CSSNumericType");

    // 2. Let idlDict be an empty ordered map, representing a dictionary of type D.
    // 3. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries, in order from least to most derived.
    // 4. For each dictionary dictionary in dictionaries, in order:
    // NB: We defer construction until the return initializer because some members may not be default-constructible. Inherited dictionaries are represented by the generated C++ struct inheritance.

    // 5. Return idlDict.
    return CSSNumericType {
        .angle = TRY([&]() -> JS::ThrowCompletionOr<Optional<WebIDL::Long>> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("angle"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return WebIDL::convert_to_int<WebIDL::Long>(vm, js_member_value, WebIDL::EnforceRange::No, WebIDL::Clamp::No); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 7. Otherwise, jsMemberValue is undefined and the member is optional.
            return OptionalNone {};
        }()),
        .flex = TRY([&]() -> JS::ThrowCompletionOr<Optional<WebIDL::Long>> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("flex"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return WebIDL::convert_to_int<WebIDL::Long>(vm, js_member_value, WebIDL::EnforceRange::No, WebIDL::Clamp::No); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 7. Otherwise, jsMemberValue is undefined and the member is optional.
            return OptionalNone {};
        }()),
        .frequency = TRY([&]() -> JS::ThrowCompletionOr<Optional<WebIDL::Long>> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("frequency"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return WebIDL::convert_to_int<WebIDL::Long>(vm, js_member_value, WebIDL::EnforceRange::No, WebIDL::Clamp::No); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 7. Otherwise, jsMemberValue is undefined and the member is optional.
            return OptionalNone {};
        }()),
        .length = TRY([&]() -> JS::ThrowCompletionOr<Optional<WebIDL::Long>> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("length"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return WebIDL::convert_to_int<WebIDL::Long>(vm, js_member_value, WebIDL::EnforceRange::No, WebIDL::Clamp::No); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 7. Otherwise, jsMemberValue is undefined and the member is optional.
            return OptionalNone {};
        }()),
        .percent = TRY([&]() -> JS::ThrowCompletionOr<Optional<WebIDL::Long>> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("percent"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return WebIDL::convert_to_int<WebIDL::Long>(vm, js_member_value, WebIDL::EnforceRange::No, WebIDL::Clamp::No); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 7. Otherwise, jsMemberValue is undefined and the member is optional.
            return OptionalNone {};
        }()),
        .percent_hint = TRY([&]() -> JS::ThrowCompletionOr<Optional<CSSNumericBaseType>> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("percentHint"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return convert_to_idl_value_for_css_numeric_base_type(vm, js_member_value); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 7. Otherwise, jsMemberValue is undefined and the member is optional.
            return OptionalNone {};
        }()),
        .resolution = TRY([&]() -> JS::ThrowCompletionOr<Optional<WebIDL::Long>> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("resolution"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return WebIDL::convert_to_int<WebIDL::Long>(vm, js_member_value, WebIDL::EnforceRange::No, WebIDL::Clamp::No); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 7. Otherwise, jsMemberValue is undefined and the member is optional.
            return OptionalNone {};
        }()),
        .time = TRY([&]() -> JS::ThrowCompletionOr<Optional<WebIDL::Long>> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("time"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return WebIDL::convert_to_int<WebIDL::Long>(vm, js_member_value, WebIDL::EnforceRange::No, WebIDL::Clamp::No); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 7. Otherwise, jsMemberValue is undefined and the member is optional.
            return OptionalNone {};
        }()),
    };
}

} // namespace Web::Bindings
