@function __get-callback(
  $function: __identity,
  $this-arg: null,
  $arg-count: null
) {
  $result: if(
    $arg-count,
    __exec("__base-callback", $function, $this-arg, $arg-count),
    "__base-callback"
  );

  @return $result;
}

@function __bind-callback-1($value) {
  $function: __this("function");
  $this-arg: __this("this-arg");

  @return __call($function, $this-arg, $value);
}

@function __bind-callback-3($value, $index, $collection) {
  $function: __this("function");
  $this-arg: __this("this-arg");

  @return __call($function, $this-arg, $value, $index, $collection);
}

@function __bind-callback-4($accumulator, $value, $index, $collection) {
  $function: __this("function");
  $this-arg: __this("this-arg");

  @return __call(
    $function,
    $this-arg,
    $accumulator,
    $value,
    $index,
    $collection
  );
}

@function __bind-callback-5($value, $other, $key, $map, $source) {
  $function: __this("function");
  $this-arg: __this("this-arg");

  @return __call($function, $this-arg, $value, $other, $key, $map, $source);
}

@function __bind-callback-any($arguments...) {
  $function: __this("function");
  $this-arg: __this("this-arg");

  @return __call($function, $this-arg, $arguments...);
}

@function __bind-callback($function, $this-arg: null, $arg-count: null) {
  @if (not __function-exists($function)) {
    @return "__identity";
  }

  @if ($this-arg == null) {
    @return $function;
  }

  $_: __scope(
    (
      "function": $function,
      "this-arg": $this-arg
    )
  );

  $arg-count-case: (
    1: "__bind-callback-1",
    3: "__bind-callback-3",
    4: "__bind-callback-4",
    5: "__bind-callback-5"
  );

  @if ($arg-count > 5) {
    @return "__bind-callback-any";
  }

  $result: __bind(__get($arg-count-case, $arg-count), $this-arg);
  $_: __scope(false);

  @return $result;
}

@function __base-callback-shifted($value, $args...) {
  $function: __this("function");
  $this-arg: __this("this-arg");
  $shifted-arguments: __this("shifted-arguments");

  $arguments: set-nth($shifted-arguments, 1, $value);

  @return __call($function, $this-arg, $arguments...);
}

@function __base-callback(
  $function: __identity,
  $this-arg: $__undefined__,
  $arg-count: null
) {
  $type: if(__function-exists($function), "function", type-of($function));

  @if ($function == null) {
    @return "__identity";
  }

  @if $type == "list" {
    $shifted-arguments: set-nth($function, 1, null);
    $function: nth($function, 1);
    $_: __scope(
      (
        "function": $function,
        "this-arg": $this-arg,
        "shifted-arguments": $shifted-arguments
      )
    );
    $base-callback-shifted: __bind("__base-callback-shifted");
    $_: __scope(false);

    @return $base-callback-shifted;
  }

  @if __is-native($function) {
    @return __ary($function, 1);
  }

  @if $type == "function" {
    @return if(
      not __is-undefined($this-arg) and __is-function($function),
      __bind-callback($function, $this-arg, $arg-count),
      $function
    );
  }

  @return if(
    $type == "map",
    __base-matches($function, __is-falsey($arg-count)),
    __base-property($function)
  );
}

@function __callback($function: __identity, $this-arg: null, $guard: null) {
  @if ($guard and __is-iteratee-call($function, $this-arg, $guard)) {
    $this-arg: null;
  }

  @return __base-callback($function, $this-arg);
}

/// Creates a function that invokes `$function` with the `_this` binding of `$this-arg`
/// and arguments of the created function.
///
/// If `$function` is a property name the
/// created callback returns the property value for a given element.
///
/// If `$function`
/// is a map the created callback returns `true` for elements that contain
/// the equivalent map properties, otherwise it returns `false`.
///
/// If `$function` is a list the created callback returns the result of
/// the function (first item of the list) right-partially applied with the
/// rest of the list items, called with the value as the first argument (see example).
///
/// @access public
/// @group Utility
/// @param {*} $func [_identity] - The value to convert to a callback.
/// @param {*} $this-arg [null] - The `_this` binding of `$function`.
/// @returns {Function} Returns the callback.
/// @example scss
///     $fully-lighten: _callback(lighten 100%);
///     $lightened-color: _exec($fully-lighten, black);
///     // => white

@function _callback($args...) {
  @return call(get-function("__callback"), $args...);
}

@function _iteratee($args...) {
  @return call(get-function("__callback"), $args...);
}
