//
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
//

@use 'sass:list';
@use 'sass:map';
@use 'sass:meta';

/// Validates the Map returned from `tokens.md-comp-*-values()` functions.
///
/// Validation requires a `$values` Map and at least the `$supported-tokens`
/// keyword argument.
/// @example - scss
///   $tokens: validate.values(
///     md-comp-foo.values($deps, $exclude-hardcoded-values),
///     $supported-tokens: (),
///       'foo-color',
///       'foo-size',
///     ),
///   );
///
/// Additional keyword arguments for `$unsupported-tokens`, `$renamed-tokens`,
/// and `$new-tokens` are optional.
///
/// @param {Map} $values - The values Map returned from `tokens.md-comp-*-values()` functions.
/// @param {List} $supported-tokens - Keyword argument for every token that is supported.
/// @param {List} $unsupported-tokens [null] - Keyword argument for every token that is not supported and should be removed.
/// @param {Map} $renamed-tokens [null] - Keyword argument for a Map whose keys are token names and values are new token names to rename them to. The new token name must appear in the `$supported-tokens` list.
/// @param {Map} $new-tokens [null] - Keyword argument for a Map whose keys and values are new tokens to be added. They must also appear in the `$supported-tokens` list.
/// @return {Map} The `tokens.md-comp-*-values()` Map with any unsupported tokens removed, renamed tokens updated, and new tokens added.
@function values($values, $tokens...) {
    $tokens: meta.keywords($tokens);

    $renamed-tokens: map.get($tokens, 'renamed-tokens');
    @if $renamed-tokens {
        @if meta.type-of($renamed-tokens) != 'map' {
            @error 'validate.values($renamed-tokens) must be a map.';
        }

        @each $old-token, $new-token in $renamed-tokens {
            @if not map.has-key($values, $old-token) {
                @error 'validate.values($renamed-tokens: (\'#{$old-token}\': \'#{$new-token}\')) old-token in not the provided values.';
            }

            $values: map.set($values, $new-token, map.get($values, $old-token));
            $values: map.remove($values, $old-token);
        }
    }

    $unsupported-tokens: map.get($tokens, 'unsupported-tokens');
    @if $unsupported-tokens {
        @if meta.type-of($unsupported-tokens) != 'list' {
            @error 'validate.values($unsupported-tokens) must be a list.';
        }

        @each $unsupported-token in $unsupported-tokens {
            @if not map.has-key($values, $unsupported-token) {
                @error 'validate.values($unsupported-tokens: (\'#{$unsupported-token}\')) token is not in the provided values.';
            }

            $values: map.remove($values, $unsupported-token);
        }
    }

    $new-tokens: map.get($tokens, 'new-tokens');
    @if $new-tokens {
        @if meta.type-of($new-tokens) != 'map' {
            @error 'validate.values($new-tokens) must be a map.';
        }

        @each $new-token, $new-value in $new-tokens {
            @if map.has-key($values, $new-token) {
                @warn 'validate.values($new-tokens: (\'#{$new-token}\': \'#{$new-value}\')) already exists. Has it been added to tokens? If so, remove this new token.';
            }

            $values: map.set($values, $new-token, $new-value);
        }
    }

    $supported-tokens: map.get($tokens, 'supported-tokens');
    @if not $supported-tokens or meta.type-of($supported-tokens) != 'list' {
        @error 'validate.values() must include a $supported-tokens list.';
    }

    @each $token in map.keys($values) {
        @if list.index($supported-tokens, $token) == null {
            @error 'validate.values($supported-tokens) is missing the \'#{$token}\' token. Does it need adding to $unsupported-tokens?';
        }
    }

    @each $supported-token in $supported-tokens {
        @if not map.has-key($values, $supported-token) {
            @error 'The provided values are missing the supported \'#{$supported-token}\' token.';
        }
    }

    @return $values;
}
