@use 'sass:map';
@import 'tokens';

// Register current theme with a name; overwrite currently used tokens
/// @access public
/// @param {String} $theme - the token map of the theme
/// @param {String} $name - the name of the new theme
/// @param {String} $base - the theme used as a token base for the new theme
/// @example - @include($my-tokens, testTheme, aposin);
@function nx-register-theme($theme, $name, $base: null) {
    @if ($base == null) {
        $nx-theme: map.merge($nx-theme, $theme) !global;
    } @else {
        $parent-theme: map.get($nx-themes, $base);
        @if ($parent-theme == null) {
            @error 'Theme: parent theme is not defined.';
        }
        // overwrite the already existing tokens by merging:
        // new tokens + overwritten base theme tokens + base theme tokens
        // global is needed to change a variable value from a local scope
        $nx-theme: map.merge($parent-theme, $theme) !global;
    }

    $nx-themes: add-theme($nx-themes, $name, $nx-theme) !global;
    @return $nx-themes;
}

// Compile theme tokens to css
/// @access public
/// @returns injected into the :root scope css variable declarations
/// @param {String} $theme-name - the name of theme to build
/// @example -  @include nx-build-theme(aposin)
@mixin nx-build-theme($theme-name: aposin) {
    $theme: map.get($nx-themes, $theme-name);

    :root,
    :host {
        @each $name, $value in $theme {
            $parentValue: map.get($theme, $value);
            @if ($parentValue != null) {
                $value: var(--#{$value});
            }
            @include _css-var($name, $value);
        }
    }
}

/// @access public
/// @returns different type styles per token.
/// @param {String} $name - the name of the token to get the styles for
/// @example -  @include type-style('body-short-01');
@mixin type-style($name) {
    font-size: v(#{$name}-font-size);
    line-height: v(#{$name}-line-height);
    font-weight: v(#{$name}-font-weight);
    letter-spacing: v(#{$name}-letter-spacing);
}

// Create a custom property declaration
/// @access private
@mixin _css-var($name, $value) {
    #{"--"+$name}: #{$value};
}

// Retired helper to get css var with a fallback value
// Now basically a wrapper for css var function.
@function v($token) {
    $value: _nx-deep-find-value($nx-theme, $token, map.get($nx-theme, $token));

    @if ($value == null) {
        @error 'Theme: cannot find value for key `' + $token + '`';
    }

    @return var(--#{$token});
}

/// Gets a css variable with a fallback value
/// @access public
/// @param {String} $token - token key to be used
/// @returns var(--<$token>, <fallback-value>
/// @example - background: nx-theme(primary-action)
@function nx-theme($token) {
    @return v($token);
}

// Helper function to make it possible to reuse keys from the same map.
// Example:
// $theme: (
//    global-color: #123456,
//    component-color: global-color
// );
// This makes it possible to reuse "global-color" inside the theming map.
// Functions and mixins like nx-theme(), v() and var() will take care of resolving
// these references
/// @access private
@function _nx-deep-find-value($theme, $key, $value) {
    $parent-value: map.get($theme, $value);

    @if ($parent-value != null) {
        @return _nx-deep-find-value($theme, $value, $parent-value);
    }

    @return $value;
}

// Adds a new theme to the list of available themes
@function add-theme($nx-themes, $key, $value: null) {
    $new-theme: (
        $key: $value,
    );
    @return map.merge($nx-themes, $new-theme);
}
