@use 'sass:map';
@use 'sass:meta' as meta;
@use 'sass:string';
@use 'sass:list';
@use '../utils/meta' as *;
@use './types';
@use '../themes/mixins' as *;
@use '../config' as *;

////
/// @package theming
/// @group Typography
////

/// Normalizes a font-family value for use in CSS custom properties.
/// If the value is a Sass string, it is unquoted to produce raw CSS tokens
/// so that a stringified font list like "'Comic Sans MS', sans-serif"
/// is emitted correctly. All other types (lists, var() expressions,
/// keywords like inherit, etc.) are returned as-is.
/// @access private
/// @param {String | List | *} $value - The font-family value to normalize.
/// @return {String | List | *} The normalized value.
@function _normalize-font-family($value) {
    @return if(meta.type-of($value) == 'string', string.unquote($value), $value);
}

/// Includes all category related styles into the current style block.
/// @param {String} $category - The type scale category.
/// @param {String} $check [true] - Toggles category checks against ITypeScale. Set to false when using a custom type scale.
/// @example scss - Add the `h1` styles to custom CSS selector.
///   .fancy-h1 {
///      @include type-style('h1');
///
///      color: mediumvioletred;
///   }
@mixin type-style($category, $check: true) {
    $valid: if($check, list.index(types.$ITypeScale, $category), true);

    @if $valid {
        @each $key, $value in types.$ITypeStyle {
            @if #{$key} == 'font-family' {
                font-family: var(--ig-#{$category}-#{$key}, var(--ig-font-family));
            } @else {
                #{$key}: var(--ig-#{$category}-#{$key});
            }
        }
        @content;
    } @else {
        @warn '#{$category} is not a valid type style';
    }
}

/// Transforms a type style map into css font variables.
/// @param {String} $name - The custom CSS variable name.
/// @param {Map} $type-style - A type style map reference as produced by type-style.
/// @example scss - Assign the `h1` styles to custom CSS property using the CSS font shorthand syntax.
///   $h1-style: type-scale-category($type-scale, 'h1');
///   @include type-style-vars('h1', $h1-style);
@mixin type-style-vars($name, $type-style) {
    @each $key, $value in map.remove($type-style, '_meta') {
        @if $key == 'font-family' and $value != inherit {
            --ig-#{$name}-#{$key}: #{$value};
        }

        @if $key != 'font-family' {
            --ig-#{$name}-#{$key}: #{$value};
        }
    }
}

/// Styles all native elements that match the IElementCategories map.
/// @example scss - Add type styles to all native elements.
///   .ig-typography {
///     @include type-style-elements();
///   }
/// @requires {mixin} type-style
/// @requires $ITypeScale
/// @requires $IElementCategories
@mixin type-style-elements() {
    @each $category in types.$ITypeScale {
        // Get the native element that uses typographic styles directly
        // as mapped in the $IElementCategories
        $e: map.get(types.$IElementCategories, $category);

        // Add native element typographic styles.
        @if $e {
            #{$e} {
                @include type-style($category);
            }
        }
    }
}

/// Creates CSS classes with style rules for all categories in the ITypeScale map.
/// @example scss - Create CSS classes for all type styles.
///   .ig-typography {
///     @include type-style-classes();
///   }
/// @requires {mixin} type-style
/// @requires $ITypeScale
@mixin type-style-classes() {
    @each $category in types.$ITypeScale {
        // Add class selector typographic styles.
        & &__#{$category} {
            @include type-style($category);
        }
    }
}

/// Transforms a type style map into a font style rule assigned to a css variable.
/// @param {String} $name - The custom CSS variable name.
/// @param {Map} $type-style - A type style map reference as produces by type-style.
/// @param {String} $prefix [null] - Optional prefix.
/// @example scss - Assign the `h1` styles to custom CSS property using the CSS font shorthand syntax.
///   $h1-style: type-scale-category($type-scale, 'h1');
///   @include font-var('h1-font', $h1-style);
@mixin font-var($name, $type-style, $prefix: null) {
    $ff: var(--ig-font-family);
    $fz: map.get($type-style, 'font-size');
    $fw: map.get($type-style, 'font-weight');

    // unused for now
    $fs: map.get($type-style, 'font-style');
    $lh: map.get($type-style, 'line-height');
    $tt: map.get($type-style, 'text-transform');
    $ls: map.get($type-style, 'letter-spacing');
    $mt: map.get($type-style, 'margin-top');
    $mb: map.get($type-style, 'margin-bottom');

    @include css-vars-from-theme((#{$name}: #{$fw $fz $ff}, _meta: (name: $prefix)));
}

/// Adds typography styles for h1-h6, paragraph and creates custom typography class selectors.
/// The produced styles are based on the passed typeface and type scale.
/// @access public
/// @param {String | List} $font-family - The font family to be used across all typographic elements.
/// @param {Map} $type-scale - A type scale map as produced by the type-scale function.
/// @example scss - Using a font-family list
///   @include typography(("Open Sans", Helvetica, sans-serif), $my-type-scale);
/// @example scss - Using a quoted string (backward compatible)
///   @include typography("'Comic Sans MS', sans-serif", $my-type-scale);
/// @example scss - Using a CSS variable reference
///   @include typography(var(--my-custom-font), $my-type-scale);
///
/// @requires {function} is-root
/// @requires {mixin} type-style-vars
/// @requires {mixin} type-style-classes
/// @requires {mixin} type-style-elements
/// @requires {function} is-root
/// @requires {function} is-host
@mixin typography($font-family, $type-scale) {
    $root: is-root();
    $host: is-host();
    $scope: if($root, ':root', '&');

    #{$scope} {
        --ig-font-family: #{_normalize-font-family($font-family)};
        --ig-base-font-size: #{$base-font-size};

        @each $name, $style in map.remove($type-scale, '_meta') {
            @include type-style-vars($name, $style);
        }
    }

    @if $root or $host {
        .ig-typography {
            @include type-style-elements();
            @include type-style-classes();
        }
    }
}
