// Copyright (c) 2025 MatteuSan and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

@use '../tools';
@use 'tokens';
@use 'sass:list';
@use 'sass:map';
@use 'sass:meta';
@use 'sass:string';

@use 'validators';

/// Stores the prefix for the design system.
/// @access public
$ds-prefix: '' !default;

/// Stores the separator for the design system.
/// @access public
$ds-separator: '-' !default;

/// Validation for keys.
/// @access public
$validation: true !default;

/// Determines if errors contain extra information (i.e. valid values)
/// @access public
$verbose: false !default;

/// Switches the leniency of the passed values to the keys.
/// @access public
$strict-values: false !default;

/// Stores the keys for getting raw values.
/// @access private
$_ds-key-registry: ();

/// Creates a key for a css property of a component.
/// @param {string} $key
/// @param {*} $value
/// @return {string} returns var() with key and fallback value.
@function create($key, $value) {
  $_ds-key-registry: map.set($_ds-key-registry, $key, _sanitize-value($value)) !global;
  @return var(--#{_sanitize-prefix($ds-prefix)}#{_sanitize-key-in-custom-property($key)}, #{_sanitize-value($value)});
}

/// Retrieves an existing component key.
/// @param {string} $key
/// @return {string} returns var() with key.
@function get($key) {
  @if _is-registered-key($key) {
    @return var(--#{_sanitize-prefix($ds-prefix)}#{_sanitize-key-in-custom-property($key)});
  }
  @if not $validation {
    @return var(--#{_sanitize-prefix($ds-prefix)}#{_sanitize-key-in-custom-property($key)});
  }
}

/// Retrieves the raw value of an existing component key.
/// @param {string} $key
/// @return {string} returns var() with key.
@function get-raw($key) {
  @if _is-registered-key($key) {
    $raw: tools.string-replace('#{tools.string-replace('#{map.get($_ds-key-registry, $key)}', ')', '')}', 'var(--', '');
    @if tokens.check($raw) {
      @return tokens.switch-raw($raw);
    }
    @return map.get($_ds-key-registry, $key);
  }
  @if not $validation {
    @return map.get($_ds-key-registry, $key);
  }
}

/// Checks if the query is a valid registered key. This is a different method from the private one.
/// @param {*} query
/// @return {boolean} validated query.
@function check($query) {
  @return _is-registered-key($query, false);
}

/// Binds the a value to the component key.
/// @param {string} $key
/// @param {*} $value
/// @param {boolean} $important
/// @return {string|*} returns an overrider css custom property with a new value.
@mixin bind($key, $value, $important: false) {
  @if $important {
    --#{_sanitize-prefix($ds-prefix)}#{_sanitize-key-in-custom-property($key)}: #{_sanitize-value($value)} !important;
  } @else {
    --#{_sanitize-prefix($ds-prefix)}#{_sanitize-key-in-custom-property($key)}: #{_sanitize-value($value)};
  }
  @if not check($key) {
    @include _register-key($key, $value);
  }
}

/// Retrieves the entire key registry.
/// @param {list<string>} $targets
/// @return {list<string>} list of registered keys.
@function registry-get($targets: (), $exceptions: ()) {
  @if not validators.is-empty($targets) {
    $targeted-keys: ();
    @each $target in $targets {
      @each $key in map.keys($_ds-key-registry) {
        @if string.index($key, $target) == 1 {
          $targeted-keys: list.append($targeted-keys, $key, 'comma');
        }
      }
    }
    @return $targeted-keys;
  }

  @if not validators.is-empty($exceptions) {
    $targeted-keys: ();
    @each $key in map.keys($_ds-key-registry) {
      @if not list.index($exceptions, $key) {
        $targeted-keys: list.append($targeted-keys, $key, 'comma');
      }
    }
    @return $targeted-keys;
  }

  @return map.keys($_ds-key-registry);
}

/// Sanitizes the value passed to the key.
/// @access private
/// @param {*} $value
/// @return {*} sanitized value.
@function _sanitize-value($value) {
  @if $strict-values {
    @return tokens.get($value);
  }
  @return tokens.switch($value);
}

/// Sanitizes and verifies a prefix.
/// @access private
/// @param {string} $prefix
/// @return {string} sanitized and validated prefix.
@function _sanitize-prefix($prefix) {
  @if validators.is-empty($prefix) {
    @return '';
  }
  @return '#{$prefix}-';
}

/// Sanitizes and verifies a key.
/// @access private
/// @param {string} $key
/// @return {string} sanitized and validated context.
@function _sanitize-key-in-custom-property($key) {
  @if $ds-separator != '-' {
    @return tools.string-replace($key, $ds-separator, '-');
  }
  @return $key;
}

/// Registers a key to the key registry variable.
/// @access private
/// @param {string} $key
/// @param {*} $value
/// @return {void} key registry
@mixin _register-key($key, $value) {
  $_ds-key-registry: map.set($_ds-key-registry, $key, tokens.switch($value)) !global;
}

/// Validates a query if it's a registered key or not
/// @access private
/// @param {string} $query
/// @return {boolean|error} is registered key
@function _is-registered-key($query, $errors: $validation) {
  @if not list.index(map.keys($_ds-key-registry), $query) {
    @if $errors {
      @if $verbose {
        @error 'Invalid key: #{$query}. Please use a valid registered key: #{map.keys($_ds-key-registry)}';
      }
      @error 'Invalid key: #{$query}.';
    } @else {
      @return false;
    }
  } @else {
    @return true;
  }
}