using System.Dynamic;
using System.Reflection;
using Jint.Native;

#pragma warning disable IL2092

namespace Jint.Runtime.Interop.Reflection;

internal sealed class DynamicObjectAccessor : ReflectionAccessor
{
    private JintSetMemberBinder? _setter;
    private JintGetMemberBinder? _getter;

    public DynamicObjectAccessor(
        Type memberType,
        PropertyInfo? indexer = null) : base(memberType, indexer)
    {
    }

    public override bool Writable => true;

    protected override object? DoGetValue(object target, string memberName)
    {
        var dynamicObject = (DynamicObject) target;
        var getter = _getter ??= new JintGetMemberBinder(memberName, ignoreCase: true);
        dynamicObject.TryGetMember(getter, out var result);
        return result;
    }

    protected override void DoSetValue(object target, string memberName, object? value)
    {
        var dynamicObject = (DynamicObject) target;
        var setter = _setter ??= new JintSetMemberBinder(memberName, ignoreCase: true);
        dynamicObject.TrySetMember(setter, value);
    }

    protected override object? ConvertValueToSet(Engine engine, object value)
    {
        // we expect value to be generally CLR type, convert when possible
        return value switch
        {
            JsBoolean jsBoolean => jsBoolean._value ? JsBoolean.BoxedTrue : JsBoolean.BoxedFalse,
            JsString jsString => jsString.ToString(),
            JsNumber jsNumber => jsNumber._value,
            JsNull => null,
            JsUndefined => null,
            _ => value
        };
    }

    private sealed class JintGetMemberBinder : GetMemberBinder
    {
        public JintGetMemberBinder(string name, bool ignoreCase) : base(name, ignoreCase)
        {
        }

        public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject? errorSuggestion)
        {
            throw new NotImplementedException(nameof(FallbackGetMember) + " not implemented");
        }
    }

    private sealed class JintSetMemberBinder : SetMemberBinder
    {
        public JintSetMemberBinder(string name, bool ignoreCase) : base(name, ignoreCase)
        {
        }

        public override DynamicMetaObject FallbackSetMember(
            DynamicMetaObject target,
            DynamicMetaObject value,
            DynamicMetaObject? errorSuggestion)
        {
            throw new NotImplementedException(nameof(FallbackSetMember) + " not implemented");
        }
    }
}
