@function __base-merge-deep($map, $source, $key, $merge-function, $customizer, $stack-a: (), $stack-b: ()) {
    $length: length($stack-a);
    $src-value: __get($source, $key);

    @while $length > 0 {
        @if __is-equal(nth($stack-a, $length), $src-value) {
            $map: __set($map, $key, nth($stack-b, $length));

            @return $map;
        }

        $length: $length - 1;
    }

    $value: __get($map, $key);
    $result: if($customizer,
        __exec($customizer, $value, $src-value, $key, $map, $source),
        $__undefined__);
    $is-common: __is-undefined($result);

    @if $is-common {
        $result: $src-value;

        @if __is-list($src-value) {
            $result: if(__is-list($value),
                $value,
                __either($value, ()));
        } @else if __is-plain-map($src-value) or __is-arglist($src-value) {
            $result: if(__is-arglist($value),
                __to-map($value),
                if(__is-plain-map($value), $value, ()));
        } @else {
            $is-common: false;
        }
    }

    $stack-a: append($stack-a, $src-value);
    $stack-b: append($stack-b, $result);

    @if $is-common {
        $_: __this('map', __set($map, $key, __exec($merge-function, $result, $src-value, $customizer, $stack-a, $stack-b)));
    } @else if not __is-equal($result, $value) {
        $_: __this('map', __set($map, $key, $result));
    }

    @return __this('map');
}


@function __base-merge-function($src-value, $key, $source) {
    $map: __this('map');
    $customizer: __this('customizer');
    $stack-a: __this('stack-a');
    $stack-b: __this('stack-b');
    $is-source-list: __this('is-source-list');

    @if __is-map-like($src-value) {
        $base-merge-deep: __bind('__base-merge-deep');

        @return __exec($base-merge-deep, $map, $source, $key, __base-merge, $customizer, $stack-a, $stack-b);
    }

    $value: __get($map, $key);
    $result: if($customizer,
        __exec($customizer, $value, $src-value, $key, $map, $source),
        $__undefined__);
    $is-common: __is-undefined($result);

    @if $is-common {
        $result: $src-value;
    }

    @if ($is-source-list or not __is-undefined($result))
    and ($is-common or not __is-equal($result, $value)) {
        $map: __this('map', __set($map, $key, $result));
    }

    @return true;
}


@function __base-merge($map, $source, $customizer: null, $stack-a: (), $stack-b: ()) {
    @if not __is-map-like($map) {
        @return $map;
    }

    $is-source-list: __is-list($source);
    $func: if($is-source-list, '__list-each', '__base-for-own');
    $_: __scope((
        'map': $map,
        'customizer': $customizer,
        'stack-a': $stack-a,
        'stack-b': $stack-b,
        'is-source-list': $is-source-list
    ));
    $base-merge-function: __bind('__base-merge-function');
    $_: __exec($func, $source, $base-merge-function);
    $map: __this('map');
    $_: __scope(false);

    @return $map;
}


@function __list-merge($list, $source, $customizer: null) {
    // todo
}


@function __merge($arguments...) {
    $assigner: __create-assigner('__base-merge');

    @return __exec($assigner, $arguments...);
}


/// Recursively merges own enumerable properties of the source map(s), that
/// don't resolve to `undefined` into the destination map. Subsequent sources
/// overwrite property assignments of previous sources. If `$customizer` is
/// provided, it is invoked to produce the merged values of the destination and
/// source properties. If `$customizer` returns `undefined` merging is handled
/// by the method instead. The `$customizer` is bound to `$this-arg` and invoked
/// with five arguments; (source-value, other-value, key, map, source).
///
///
/// @access public
/// @group Map
/// @param {Map} $map The destination map.
/// @param {Map...} $source... The source maps.
/// @param {Function} $customizer [null] The function to customize merging properties.
/// @param {*} $this-arg [null] - The `_this` binding of `$customizer`.
/// @returns {Map} Returns `map`.
/// @example scss
/// $users: (
///   'data': (( 'user': 'barney' ), ( 'user': 'fred' ))
/// );
/// $ages: (
///   'data': (( 'age': 36 ), ( 'age': 40 ))
/// );
/// $foo: _merge($users, ages);
/// // => ( 'data': (( 'user': 'barney', 'age': 36 ), ( 'user': 'fred', 'age': 40 )) )
/// 
/// // using a customizer callback
/// // todo

@function _merge($args...) {
    @return call(get-function('__merge'), $args...);
}
