@use 'sass:color';
@use 'sass:list';
@use 'sass:map';
////
/// @group site.variables
////

@use '../utils' as *;
@use 'config' as *;

// ----------------------------------------------------------------------------
// API
// ----------------------------------------------------------------------------

@mixin hsl-vars($name, $color) {
    #{$name}: #{$color};
    #{$name}-h: #{round-d(color.channel($color, 'hue', $space: hsl), 2)};
    #{$name}-s: #{round-d(color.channel($color, 'saturation', $space: hsl), 2)};
    #{$name}-l: #{round-d(color.channel($color, 'lightness', $space: hsl), 2)};
}

@mixin rgb-vars($name, $color) {
    #{$name}: #{$color};
    #{$name}-rgb:
        #{rgb-red($color)},
        #{rgb-green($color)},
        #{rgb-blue($color)};
}

/// Generic mixin that takes a map of variables and outputs them as css variables.
///
/// @param {*} $vars A map containing value pairs which will be output as css variables. Any color
/// variable that ends with -hsla will will also get h/s/l/a component variables.

@mixin css-vars($vars, $variant: null) {
    @each $name, $value in $vars {
        // converter conflicting color names to strings
        $name: to-str($name);

        // if the value is a map, we create a new prefix using the key and output the variables in the map

        @if (is-map($value)) {
            // try to get the value by variant
            @if ($variant) {
                @if (map.has-key($value, $variant)) {
                    $value: map.get($value, $variant);
                } @else {
                    @warn #{'The variant' $variant 'does not exist for the variable' $name};
                }
            }

            // with no variant specififed just get the first value
            @else {
                $value: map.get($value, list.nth(map.keys($value), 1));
            }
        }

        // vars that end with 'hsla' generate individual hsla vars

        @if (is-color($value) and ends-with($name, '-hsla')) {
            $name: str-replace($name, '-hsla');
            $name: site-var-name($name);
            @include hsl-vars($name, $value);
        }

        // vars that end with 'rgb' generate an rgb values var

        @else if (is-color($value) and ends-with($name, '-rgb')) {
            $name: str-replace($name, '-rgb');
            $name: site-var-name($name);
            @include rgb-vars($name, $value);
        }

        // output the unmodified variable
        @else {
            @if ($value != null) {
                $name: site-var-name($name);
                #{$name}: #{$value};
            }
        }
    }
}

/// Function that produces an adjusted color value using hsla syntax for hsla variables. For a given
/// value of $name, it expects the following variables to be defined:
///
///     $--$name (the base color)
///     $--$name-h (the hue component)
///     $--$name-s (the saturation component)
///     $--$name-l (the lightness component)
///
/// These variables can be defined using the hsl-var-def mixin. T
///
/// @example background: hsl-var('primary', $alpha: 0.5, $s-adj: 10%, $l-adj: -10%);

@function hsl-var(
    $name,
    $alpha: null,
    $h: null,
    $s: null,
    $l: null,
    $dh: null,
    $ds: null,
    $dl: null
) {
    @if ($h or $s or $l or $dh or $ds or $dl or $alpha) {
        $h: if-null($h, var(--#{$name}-h));
        $s: if-null($s, var(--#{$name}-s));
        $l: if-null($l, var(--#{$name}-l));

        @if ($dh) {
            $h: calc-add(#{$h}, $dh);
        }

        @if ($ds) {
            $s: calc-add(#{$s}, $ds);
        }

        @if ($dl) {
            $l: calc-add(#{$l}, $dl);
        }

        @if ($alpha) {
            @return hsla(#{$h}, #{$s}, #{$l}, $alpha);
        }

        @return hsla(#{$h}, #{$s}, #{$l}, 1);
    }

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

/// Outputs configured site variables as css variables. Intended to be used to established variables
/// typically once per page or site.
///
/// So here's a what a typical config might look like:
///
/// ```scss
/// @@use 'variables' as * with (
///     $-config: (
///
///         vars-prefix: 'theme',
///         vars: (
///             page-bg-hsla: white,
///             page-fg-hsla: black,
///             green-hsla: (
///                 base: green,
///                 dark: lightgreen,
///             ),
///             orange-rgb: (
///                 base: rgb(208, 122, 18),
///                 dark: rgb(247, 196, 133),
///             )
///         )
///     )
/// );
/// ```
///
/// Note that you can also set $-vars-prefix and $-vars directly in the use definition instead of using $-config.
///
/// Then you can use the mixin to output the variables like this:
///
/// ```scss
/// .body {
///    // light mode
///    @include site-vars();
///
///     // dark mode
///     @media (prefers-color-scheme: dark) {
///         @include site-vars('dark');
///     }
/// }
/// ```
///
/// And finally you can access a variable like this:
///
/// ```css
/// .selector {
///     color: site-var('page-fg');
/// }
/// ```

@mixin site-vars($variant: null) {
    $vars: get('vars');
    @include css-vars($vars, $variant);
}

@mixin site-var-add($nameOrMap, $value) {
    @if (is-map($nameOrMap)) {
        @each $name, $value in $nameOrMap {
            @include site-var-add($name, $value);
        }
    } @else {
        @include config('vars.' + $nameOrMap, $value);
    }
}

@function site-var-name($name, $dashes: true) {
    $vars-prefix: get('vars-prefix', null);
    $d: if(sass($dashes): '--' ; else: '');
    $p: if(sass($vars-prefix): $vars-prefix + '-' ; else: '');

    @return #{$d}#{$p}#{'' + $name};
}

/// Returns true of the given varianble exists in site variables. It's aware of the fact that a
/// valid variable may have been defined / as a color with hsla and rgb components.

@function site-var-exists($name) {
    $vars: get('vars');

    @return map.has-key($vars, $name) or map.has-key($vars, $name + '-hsla') or
        map.has-key($vars, $name + '-rgb');
}

/// Produces css variable syntax for a site variable. Verifies that the variable exists and produces
/// a warning if not found.

@function site-var($name, $alpha: null, $dh: null, $ds: null, $dl: null) {
    $has-alpha: $alpha != null;
    $has-adjustments: ($dh != null or $ds != null or $dl != null);

    @if (site-var-exists($name + '-hsla') and ($has-alpha or $has-adjustments)) {
        @return site-var-hsla($name, $alpha, $dh, $ds, $dl);
    }

    @if (site-var-exists($name + '-rgb') and $has-alpha) {
        @return site-var-rgb($name, $alpha);
    }

    @if ($has-alpha or $has-adjustments) {
        @warn #{'The site variable "#{$name}" has not been defined or does not support alpha or color adjustments. Just add "-rgb" or "-hsla" to the end of the variable name to support this.'};
    }

    @if (site-var-exists($name)) {
        @return var(#{site-var-name($name)});
    }

    // key wasn't found
    @else {
        @warn #{'The site variable "#{$name}" has not been defined.'};
        @return null;
    }
}

/// Produces css variable syntax for an hsla variable and can produce the relative complex syntax
/// required to adjust alhpa and hsla components.

@function site-var-hsla($name, $alpha: null, $dh: null, $ds: null, $dl: null) {
    @if (site-var-exists($name + '-hsla')) {
        $name: site-var-name($name, false);
        @return hsl-var($name, $alpha: $alpha, $dh: $dh, $ds: $ds, $dl: $dl);
    }

    // key wasn't found
    @else {
        @warn #{'The site variable "#{$name}-hsla" has not been defined.'};
        @return null;
    }
}

/// Produces css variable syntax for an rgb variable including an adjust alpha value.

@function site-var-rgb($name, $alpha: 1) {
    @if (site-var-exists($name + '-rgb')) {
        $rgb: site-var($name + '-rgb');
        @return rgba($rgb, $alpha);
    }

    // key wasn't found
    @else {
        @warn #{'The site variable "#{$name}-hsla" has not been defined.'};
        @return null;
    }
}
