@use 'sass:meta';
@use 'sass:map';
@use '../core/tokens/m2-utils';
@use '../core/theming/theming';
@use '../core/theming/inspection';
@use '../core/style/sass-utils';
@use '../core/style/elevation';

// Tokens that can't be configured through Angular Material's current theming API,
// but may be in a future version of the theming API.
@function get-unthemable-tokens() {
  @return (
    button-filled-container-shape: 4px,
    button-filled-horizontal-padding: 16px,
    button-filled-icon-offset: -4px,
    button-filled-icon-spacing: 8px,
    button-outlined-container-shape: 4px,
    button-outlined-horizontal-padding: 15px, // Normally it's 16px, but -1px for the outline.
    button-outlined-icon-offset: -4px,
    button-outlined-icon-spacing: 8px,
    button-outlined-keep-touch-target: false,
    button-outlined-outline-width: 1px,
    button-protected-container-elevation-shadow: elevation.get-box-shadow(2),
    button-protected-container-shape: 4px,
    button-protected-disabled-container-elevation-shadow: elevation.get-box-shadow(0),
    button-protected-focus-container-elevation-shadow: elevation.get-box-shadow(4),
    button-protected-horizontal-padding: 16px,
    button-protected-hover-container-elevation-shadow: elevation.get-box-shadow(4),
    button-protected-icon-offset: -4px,
    button-protected-icon-spacing: 8px,
    button-protected-pressed-container-elevation-shadow: elevation.get-box-shadow(8),
    button-text-container-shape: 4px,
    button-text-horizontal-padding: 8px,
    button-text-icon-offset: 0,
    button-text-icon-spacing: 8px,
    button-text-with-icon-horizontal-padding: 8px,
    button-tonal-container-shape: 4px,
    button-tonal-horizontal-padding: 16px,
    button-tonal-icon-offset: -4px,
    button-tonal-icon-spacing: 8px,
  );
}

// Tokens that can be configured through Angular Material's color theming API.
@function get-color-tokens($theme) {
  $is-dark: inspection.get-theme-type($theme) == dark;

  // Ideally we would derive all values directly from the theme, but it causes a lot of regressions
  // internally. For now we fall back to the old hardcoded behavior only for internal apps.
  $outline: if(m2-utils.$private-is-internal-build,
    rgba(if($is-dark, #fff, #000), 0.12),
    inspection.get-theme-color($theme, foreground, divider)
  );

  @return (
    button-filled-container-color: inspection.get-theme-color($theme, background, card),
    button-filled-disabled-container-color:
        inspection.get-theme-color($theme, foreground, disabled-button, 0.12),
    button-filled-disabled-label-text-color:
        inspection.get-theme-color($theme, foreground, disabled-button, if($is-dark, 0.5, 0.38)),
    button-filled-disabled-state-layer-color: inspection.get-theme-color($theme, foreground, base),
    button-filled-focus-state-layer-opacity: if($is-dark, 0.24, 0.12),
    button-filled-hover-state-layer-opacity: if($is-dark, 0.08, 0.04),
    button-filled-label-text-color: inspection.get-theme-color($theme, foreground, text, 1),
    button-filled-pressed-state-layer-opacity: if($is-dark, 0.24, 0.12),
    button-filled-ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1),
    button-filled-state-layer-color: inspection.get-theme-color($theme, foreground, base),
    button-outlined-disabled-label-text-color:
        inspection.get-theme-color($theme, foreground, disabled-button, if($is-dark, 0.5, 0.38)),
    button-outlined-disabled-outline-color: $outline,
    button-outlined-disabled-state-layer-color:
        inspection.get-theme-color($theme, foreground, base),
    button-outlined-focus-state-layer-opacity: if($is-dark, 0.24, 0.12),
    button-outlined-hover-state-layer-opacity: if($is-dark, 0.08, 0.04),
    button-outlined-label-text-color: inspection.get-theme-color($theme, foreground, text, 1),
    button-outlined-outline-color: $outline,
    button-outlined-pressed-state-layer-opacity: if($is-dark, 0.24, 0.12),
    button-outlined-ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1),
    button-outlined-state-layer-color: inspection.get-theme-color($theme, foreground, base),
    button-protected-container-color: inspection.get-theme-color($theme, background, card),
    button-protected-disabled-container-color:
        inspection.get-theme-color($theme, foreground, disabled-button, 0.12),
    button-protected-disabled-label-text-color:
        inspection.get-theme-color($theme, foreground, disabled-button, if($is-dark, 0.5, 0.38)),
    button-protected-disabled-state-layer-color:
        inspection.get-theme-color($theme, foreground, base),
    button-protected-focus-state-layer-opacity: if($is-dark, 0.24, 0.12),
    button-protected-hover-state-layer-opacity: if($is-dark, 0.08, 0.04),
    button-protected-label-text-color: inspection.get-theme-color($theme, foreground, text, 1),
    button-protected-pressed-state-layer-opacity: if($is-dark, 0.24, 0.12),
    button-protected-ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1),
    button-protected-state-layer-color: inspection.get-theme-color($theme, foreground, base),
    button-text-disabled-label-text-color:
        inspection.get-theme-color($theme, foreground, disabled-button, if($is-dark, 0.5, 0.38)),
    button-text-disabled-state-layer-color: inspection.get-theme-color($theme, foreground, base),
    button-text-focus-state-layer-opacity: if($is-dark, 0.24, 0.12),
    button-text-hover-state-layer-opacity: if($is-dark, 0.08, 0.04),
    button-text-label-text-color: inspection.get-theme-color($theme, foreground, text, 1),
    button-text-pressed-state-layer-opacity: if($is-dark, 0.24, 0.12),
    button-text-ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1),
    button-text-state-layer-color: inspection.get-theme-color($theme, foreground, base),
    button-tonal-container-color: inspection.get-theme-color($theme, background, card),
    button-tonal-disabled-container-color:
        inspection.get-theme-color($theme, foreground, disabled-button, 0.12),
    button-tonal-disabled-label-text-color:
        inspection.get-theme-color($theme, foreground, disabled-button, if($is-dark, 0.5, 0.38)),
    button-tonal-disabled-state-layer-color: inspection.get-theme-color($theme, foreground, base),
    button-tonal-focus-state-layer-opacity: if($is-dark, 0.24, 0.12),
    button-tonal-hover-state-layer-opacity: if($is-dark, 0.08, 0.04),
    button-tonal-label-text-color: inspection.get-theme-color($theme, foreground, text, 1),
    button-tonal-pressed-state-layer-opacity: if($is-dark, 0.24, 0.12),
    button-tonal-ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1),
    button-tonal-state-layer-color: inspection.get-theme-color($theme, foreground, base),
  );
}

// Generates the mapping for the properties that change based on the button palette color.
@function private-get-color-palette-color-tokens($theme, $palette-name) {
  $color: inspection.get-theme-color($theme, $palette-name);
  $state-layer-color: inspection.get-theme-color($theme, $palette-name, default-contrast, 1);
  $ripple-color: inspection.get-theme-color($theme, $palette-name, default-contrast, 0.1);
  $contrast-color: inspection.get-theme-color($theme, $palette-name, default-contrast);
  $container-color: inspection.get-theme-color($theme, $palette-name, default);
  $label-text-color: inspection.get-theme-color($theme, $palette-name, default-contrast, 1);
  $ripple-opacity: 0.1;

  // Ideally we would derive all values directly from the theme, but it causes a lot of regressions
  // internally. For now we fall back to the old hardcoded behavior only for internal apps.
  @if (m2-utils.$private-is-internal-build or
      meta.type-of($contrast-color) != 'color') {
    $is-dark: inspection.get-theme-type($theme) == dark;
    $container-color: inspection.get-theme-color($theme, $palette-name);
    $contrast-tone: m2-utils.contrast-tone($container-color, $is-dark);
    $color: if($contrast-tone == 'dark', #000, #fff);
    $state-layer-color: $color;
    $ripple-color: rgba($color, 0.1);
    $label-text-color: if($contrast-tone == 'dark', #000, #fff);
  }

  // outlined-outline-color:
  // TODO: we shouldn't have to set this since it's the same as the non-palette version, however
  // there are a bunch of tests internally that depend on it. We should remove this and clean
  // up the screenshots separately.
  @return (
    button-filled-container-color: $container-color,
    button-filled-label-text-color: $label-text-color,
    button-filled-ripple-color: $ripple-color,
    button-filled-state-layer-color: $state-layer-color,
    button-outlined-label-text-color: inspection.get-theme-color($theme, $palette-name, default),
    button-outlined-outline-color: map.get(get-color-tokens($theme), button-outlined-outline-color),
    button-outlined-ripple-color:
        if(
          meta.type-of(inspection.get-theme-color($theme, $palette-name)) == color,
          rgba(inspection.get-theme-color($theme, $palette-name), $ripple-opacity),
          inspection.get-theme-color($theme, foreground, base, $ripple-opacity)),
    button-outlined-state-layer-color: inspection.get-theme-color($theme, $palette-name),
    button-protected-container-color: $container-color,
    button-protected-label-text-color: $label-text-color,
    button-protected-ripple-color: $ripple-color,
    button-protected-state-layer-color: $state-layer-color,
    button-text-label-text-color: inspection.get-theme-color($theme, $palette-name),
    button-text-ripple-color:
        if(meta.type-of(inspection.get-theme-color($theme, $palette-name)) == color,
          rgba(inspection.get-theme-color($theme, $palette-name), $ripple-opacity),
          inspection.get-theme-color($theme, foreground, base, $ripple-opacity)),
    button-text-state-layer-color: inspection.get-theme-color($theme, $palette-name),
    button-tonal-container-color: inspection.get-theme-color($theme, $palette-name, default),
    button-tonal-label-text-color:
        inspection.get-theme-color($theme, $palette-name, default-contrast, 1),
    button-tonal-ripple-color:
        inspection.get-theme-color($theme, $palette-name, default-contrast, 0.1),
    button-tonal-state-layer-color:
        inspection.get-theme-color($theme, $palette-name, default-contrast, 1),
  );
}

// Tokens that can be configured through Angular Material's typography theming API.
@function get-typography-tokens($theme) {
  $font-family: inspection.get-theme-typography($theme, button, font-family);
  $font-size: inspection.get-theme-typography($theme, button, font-size);
  $font-weight: inspection.get-theme-typography($theme, button, font-weight);
  $letter-spacing: inspection.get-theme-typography($theme, button, letter-spacing);
  $text-transform: none;

  @return (
    button-filled-label-text-font: $font-family,
    button-filled-label-text-size: $font-size,
    button-filled-label-text-tracking: $letter-spacing,
    button-filled-label-text-transform: none,
    button-filled-label-text-weight: $font-weight,
    button-outlined-label-text-font: $font-family,
    button-outlined-label-text-size: $font-size,
    button-outlined-label-text-tracking: $letter-spacing,
    button-outlined-label-text-transform: none,
    button-outlined-label-text-weight: $font-weight,
    button-protected-label-text-font: $font-family,
    button-protected-label-text-size: $font-size,
    button-protected-label-text-tracking: $letter-spacing,
    button-protected-label-text-transform: none,
    button-protected-label-text-weight: $font-weight,
    button-text-label-text-font: $font-family,
    button-text-label-text-size: $font-size,
    button-text-label-text-tracking: $letter-spacing,
    button-text-label-text-transform: none,
    button-text-label-text-weight: $font-weight,
    button-tonal-label-text-font: $font-family,
    button-tonal-label-text-size: $font-size,
    button-tonal-label-text-tracking: $letter-spacing,
    button-tonal-label-text-transform: none,
    button-tonal-label-text-weight: $font-weight,
  );
}

// Tokens that can be configured through Angular Material's density theming API.
@function get-density-tokens($theme) {
  $density-scale: theming.clamp-density(inspection.get-theme-density($theme), -3);
  $scale: theming.clamp-density(inspection.get-theme-density($theme), -3);
  $sizes: (
    0: 36px,
    -1: 32px,
    -2: 28px,
    -3: 24px,
  );

  $height: map.get($sizes, $scale);
  $touch-target-display: if($scale < -1, none, block);

  @return (
    button-filled-container-height: $height,
    button-filled-touch-target-display: $touch-target-display,
    button-outlined-container-height: $height,
    button-outlined-touch-target-display: $touch-target-display,
    button-protected-container-height: $height,
    button-protected-touch-target-display: $touch-target-display,
    button-text-container-height: $height,
    button-text-touch-target-display: $touch-target-display,
    button-tonal-container-height: $height,
    button-tonal-touch-target-display: $touch-target-display,
  );
}

// Combines the tokens generated by the above functions into a single map with placeholder values.
// This is used to create token slots.
@function get-token-slots() {
  @return sass-utils.deep-merge-all(
      get-unthemable-tokens(),
      get-color-tokens(m2-utils.$placeholder-color-config),
      get-typography-tokens(m2-utils.$placeholder-typography-config),
      get-density-tokens(m2-utils.$placeholder-density-config)
  );
}
