// Effect-style `dual()` helper: returns a function whose body reads
// `arguments.length` to dispatch data-first vs curried.
function dual(arity: number, body: any): any {
  return function () {
    if (arguments.length >= arity) {
      // Data-first path: forward all args to the body. We use a plain
      // call shape so the body still sees `arguments.length` correctly
      // through its own synthetic-arguments slot.
      const a = arguments;
      if (a.length === 2) return body(a[0], a[1]);
      if (a.length === 1) return body(a[0]);
      if (a.length === 3) return body(a[0], a[1], a[2]);
      return body();
    }
    // Curried path: capture the first arg and return a function that
    // takes the rest. Body is `(self, x) => ...`, curried form is
    // `(x) => (self) => body(self, x)`.
    const first = arguments[0];
    return function (self: any) {
      return body(self, first);
    };
  };
}

// Public API mirrors Effect's `Predicate.hasProperty`.
export const hasProperty = dual(2, (self: any, property: any) => {
  return typeof self === "object" && self !== null && property in self;
});

// Sanity: plain exported function that reads `arguments.length` directly.
// Tests the simpler "function decl with synthetic arguments" cross-module
// path — no dual wrapper.
export function getArity(): number {
  return arguments.length;
}
