@use 'sass:map';
@use 'sass:list';
@use 'sass:string';
@use './variables';
@use './functions';

/// Base styles for inputs
/// @access public
/// @group form-elements
@mixin input-base {
  @include font-base;

  margin: 0;
  padding: var(--pharos-spacing-one-half-x) var(--pharos-spacing-three-quarters-x);
  width: 100%;
  display: block;
  box-sizing: border-box;
  border: 1px solid var(--pharos-form-element-color-border-base);
  border-radius: var(--pharos-radius-base-standard);
  transition: transform var(--pharos-transition-base) 0s;
  color: var(--pharos-form-element-color-text-base);
}

/// Placeholder styles for inputs
/// @access public
/// @group form-elements
@mixin input-placeholder {
  &::placeholder {
    color: var(--pharos-form-element-color-text-placeholder);
    opacity: 1;
  }
}

/// Focus styles for inputs
/// @access public
/// @group form-elements
@mixin input-focus {
  outline: none;
  border: 2px solid var(--pharos-form-element-color-border-focus);
  transition: transform var(--pharos-transition-base) 0s;
}

/// Wrapper styles for inputs
/// @access public
/// @group form-elements
@mixin input-wrapper {
  position: relative;
  display: flex;
  align-items: center;
  width: 100%;
}

/// Styles for input's invalid state
/// @access public
/// @group form-elements
@mixin input-invalid {
  border: 2px solid var(--pharos-form-element-color-border-invalid);
}

/// Styles for input's disabled state
/// @access public
/// @group form-elements
@mixin input-disabled {
  color: var(--pharos-form-element-color-text-disabled);
  background-color: var(--pharos-form-element-color-background-disabled);
  border-color: var(--pharos-form-element-color-background-disabled);
  pointer-events: none;
}

/// Styles for input's required state
/// @access public
/// @group form-elements
@mixin input-required {
  &:required {
    box-shadow: none;
  }
}

/// Styles for input states with thicker borders
/// @access public
/// @group form-elements
@mixin input-padding-thick-border {
  padding: calc(var(--pharos-spacing-one-half-x) - 1px)
    calc(var(--pharos-spacing-three-quarters-x) - 1px);
}

/// Styles for prominent input states with thicker borders
/// @access public
/// @group form-elements
@mixin input-padding-thick-border-prominent {
  padding: calc(var(--pharos-spacing-1-x) - 1px);
}

/// Styles for inputs on a background
/// @access public
/// @group form-elements
@mixin input-on-background {
  background-color: var(--pharos-color-marble-gray-20);
  border-color: var(--pharos-color-marble-gray-20);
  color: var(--pharos-color-text-white);
  caret-color: var(--pharos-color-text-white);

  &::placeholder {
    color: var(--pharos-color-marble-gray-base);
  }
}

/// Styles for input options
/// @access public
/// @group form-elements
@mixin option-input {
  @include hidden;

  display: block;
  box-sizing: border-box;
}

/// Wrapper styles for input options
/// @access public
/// @group form-elements
@mixin option-wrapper {
  display: inline-flex;
  align-items: flex-start;
  position: relative;
}

/// Styles to hide an element
/// @access public
/// @group interaction
@mixin hidden {
  border: 0;
  clip-path: inset(50%);
  height: 1px;
  width: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
}

/// Base styles for font.
/// @param {string} $font-family [var(--pharos-font-family-sans-serif)] - The font family of the font
/// @param {string} $font-size [var(--pharos-font-size-base)] - The size of the font
/// @param {string} $font-weight [var(--pharos-font-weight-regular)] - The weight of the font
/// @param {string} $line-height [var(--pharos-line-height-medium)] - The line height of the font
/// @access public
/// @group typography
@mixin font-base(
  $font-family: var(--pharos-font-family-sans-serif),
  $font-size: var(--pharos-font-size-base),
  $font-weight: var(--pharos-font-weight-regular),
  $line-height: var(--pharos-line-height-medium)
) {
  @if $font-family == var(--pharos-font-family-sans-serif) {
    @include letter-spacing-sans-serif($font-size);
  } @else if $font-family == var(--pharos-font-family-serif) {
    @include letter-spacing-serif($font-size);
  } @else {
    @error 'Unknown font-family #{$font-family}.';
  }

  font-family: $font-family;
  font-size: $font-size;
  font-weight: $font-weight;
  line-height: $line-height;
  text-rendering: optimizelegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/// Letter spacing for serif type
/// @access public
/// @group typography
@mixin letter-spacing-serif($font-size: 1rem) {
  @if $font-size != inherit {
    letter-spacing: calc(#{$font-size} * -0.01);
  }
}

/// Letter spacing for sans-serif type
/// @access public
/// @group typography
@mixin letter-spacing-sans-serif($font-size: 1rem) {
  @if $font-size != inherit {
    letter-spacing: calc(#{$font-size} * -0.02);
  }
}

/// Base styles for labels
/// @access public
/// @group form-elements
@mixin label-base($margin-bottom: var(--pharos-spacing-one-quarter-x), $text-transform: uppercase) {
  color: var(--pharos-form-element-color-text-base);
  margin-bottom: $margin-bottom;
  display: block;
  padding: 0;
  text-transform: $text-transform;
}

/// Styles for labels of form elements
/// @access public
/// @group form-elements
@mixin form-element-label {
  @include font-base(
    $font-size: var(--pharos-form-element-size-text-label),
    $font-weight: var(--pharos-font-weight-bold),
    $line-height: var(--pharos-line-height-small)
  );
  @include label-base;
}

/// Styles for labels of grouped form elements
/// @access public
/// @group form-elements
@mixin groupable-element-label {
  @include font-base;
  @include label-base($margin-bottom: 0, $text-transform: none);

  padding-left: var(--pharos-spacing-one-quarter-x);
}

/// Styles for icon buttons.
/// @access public
/// @group buttons
@mixin icon-button {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-sizing: border-box;
  background: none;
  margin: 0;
}

/// Focus styles for most interactive components.
/// @access public
/// @group interaction
@mixin interactive-focus {
  &:focus-visible {
    outline: 2px solid var(--pharos-color-focus);
    outline-offset: 2px;
  }

  @supports not selector(:focus-visible) {
    &:focus {
      outline: 2px solid var(--pharos-color-focus);
      outline-offset: 2px;
    }
  }
}

/// Base styles for buttons.
/// @param {string} $color [var(--pharos-button-color-base-primary-text-base)] - The color of the text
/// @param {string} $background-color [vvar(--pharos-button-color-base-primary-background-base)] - The color of the background
/// @param {string} $border-color [var(--pharos-button-color-base-primary-border-base)] - The color of the border
/// @access public
/// @group buttons
@mixin button-base(
  $color: var(--pharos-button-color-base-primary-text-base),
  $background-color: var(--pharos-button-color-base-primary-background-base),
  $border-color: var(--pharos-button-color-base-primary-border-base)
) {
  @include font-base($font-weight: var(--pharos-font-weight-bold));
  @include transition-base(border-color, background, color);

  color: $color;
  background-color: $background-color;
  border: 1px solid $border-color;
  padding: var(--pharos-spacing-one-quarter-x) var(--pharos-spacing-three-quarters-x);
  border-radius: var(--pharos-radius-base-standard);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  appearance: none;

  [data-pharos-component='PharosIcon'] {
    @include transition-base(fill, stroke);

    fill: $color;
    stroke: $color;
    stroke-width: 0;
  }

  [data-pharos-component='PharosIcon']:first-child:not(:only-child) {
    margin-right: var(--pharos-spacing-one-quarter-x);
  }

  [data-pharos-component='PharosIcon']:last-child:not(:only-child) {
    margin-left: var(--pharos-spacing-one-quarter-x);
  }
}

/// Hover styles for buttons.
/// @param {string} $color [var(--pharos-button-color-base-primary-text-hover)] - The color of the text
/// @param {string} $background-color [var(--pharos-button-color-base-primary-background-hover)] - The color of the background
/// @param {string} $border-color [var(--pharos-button-color-base-primary-border-hover)] - The color of the border
/// @access public
/// @group buttons
@mixin button-hover(
  $color: var(--pharos-button-color-base-primary-text-hover),
  $background-color: var(--pharos-button-color-base-primary-background-hover),
  $border-color: var(--pharos-button-color-base-primary-border-hover)
) {
  &:hover:not([disabled]),
  &:active:not([disabled]) {
    color: $color;
    background-color: $background-color;
    border: 2px solid $border-color;
    padding: calc(var(--pharos-spacing-one-quarter-x) - 1px)
      calc(var(--pharos-spacing-three-quarters-x) - 1px);

    [data-pharos-component='PharosIcon'] {
      fill: $color;
      stroke: $color;
    }
  }
}

/// Styles for large buttons.
/// @access public
/// @group buttons
@mixin button-large {
  padding: var(--pharos-spacing-one-half-x) var(--pharos-spacing-three-quarters-x);

  &:hover:not([disabled]),
  &:active:not([disabled]) {
    padding: calc(var(--pharos-spacing-one-half-x) - 1px)
      calc(var(--pharos-spacing-three-quarters-x) - 1px);
  }
}

/// Color styles for buttons.
/// @param {string} $color [var(--pharos-button-color-base-primary-text-base)] - The color of the text
/// @param {string} $background-color [var(--pharos-button-color-base-primary-background-base)] - The color of the background
/// @param {string} $border-color [var(--pharos-button-color-base-primary-border-base)] - The color of the border
/// @access public
/// @group buttons
@mixin button-color(
  $color: var(--pharos-button-color-base-primary-text-base),
  $background-color: var(--pharos-button-color-base-primary-background-base),
  $border-color: var(--pharos-button-color-base-primary-border-base)
) {
  color: $color;
  background-color: $background-color;
  border-color: $border-color;

  [data-pharos-component='PharosIcon'] {
    fill: $color;
    stroke: $color;
  }
}

/// Base styles for links.
/// @access public
/// @group links
@mixin link-base {
  @include font-base($font-size: inherit, $line-height: inherit);
  @include underline;

  letter-spacing: inherit;
  color: var(--pharos-color-interactive-secondary);
  transition-property: color, text-decoration-color;
  transition-duration: 200ms;
  transition-timing-function: ease-in-out;
}

/// Hover style for links.
/// @access public
/// @group links
@mixin link-hover {
  &:hover {
    @include no-underline;

    color: var(--pharos-color-hover-primary);
  }
}

/// Base styles for underlines.
/// @param {string} $text-decoration-color [var(--pharos-color-interactive-secondary)] - The color of the underline
/// @access public
/// @group links
@mixin underline($text-decoration-color: var(--pharos-color-interactive-secondary)) {
  text-decoration: underline;
  text-decoration-color: $text-decoration-color;
}

/* stylelint-disable declaration-block-no-redundant-longhand-properties */
/// Removes underlines.
/// @access public
/// @group links
@mixin no-underline {
  text-decoration-style: solid;
  text-decoration-line: underline;
  text-decoration-color: transparent;
}

/// Removes bullets from lists.
/// @access public
/// @group typography
@mixin no-bullet {
  list-style-type: none;
}

/// Base styles for inline lists.
/// @param {string} $margin-right [var(--pharos-spacing-one-and-a-half-x)] - The margin between list items
/// @access public
/// @group typography
@mixin list-inline($margin-right: var(--pharos-spacing-one-and-a-half-x)) {
  @include no-bullet;

  display: flex;
  flex-wrap: wrap;
  align-items: center;

  li:not(:last-child) {
    margin-right: $margin-right;
    margin-bottom: 0;
  }
}

/// Base style for nested lists.
/// @access public
/// @group typography
@mixin list-nested {
  > li:not(:last-child) {
    margin-bottom: var(--pharos-spacing-three-quarters-x);
  }
}

/// Base styles for transitions.
/// @param {string} $properties - The transition properties to style
/// @access public
/// @group interaction
@mixin transition-base($properties...) {
  transition-property: $properties;
  transition-duration: var(--pharos-transition-duration-default);
  transition-timing-function: ease-in-out;
}

/// Base styles for content labels.
/// @access public
/// @group typography
@mixin content-label($args...) {
  @include form-element-label($args...);
}

/// Creates a media query that starts at the passed breakpoint.
/// @param {string} $breakpoint - The breakpoint to use as the minimum
/// @access public
/// @group layout
@mixin at-least($breakpoint) {
  @if map.has-key(variables.$breakpoints, $breakpoint) {
    $width: map.get(variables.$breakpoints, $breakpoint, width);

    @media screen and (min-width: $width) {
      @content;
    }
  } @else {
    @error 'Unknown breakpoint `#{$breakpoint}`. ' + 'Available breakpoints are: #{map.keys(variables.$breakpoints)}.';
  }
}

/// Creates a media query that ranges up to the passed breakpoint.
/// @param {string} $breakpoint - The breakpoint to use as the maximum
/// @access public
/// @group layout
@mixin until($breakpoint) {
  @if map.has-key(variables.$breakpoints, $breakpoint) {
    $width: map.get(variables.$breakpoints, $breakpoint, width);

    @media screen and (max-width: $width) {
      @content;
    }
  } @else {
    @error 'Unknown breakpoint `#{$breakpoint}`. ' + 'Available breakpoints are: #{map.keys(variables.$breakpoints)}.';
  }
}

/// Creates a media query that ranges between the two passed breakpoints.
/// @param {string} $lower - The breakpoint to use as the lower bounds
/// @param {string} $upper - The breakpoint to use as the upper bounds
/// @access public
/// @group layout
@mixin between($lower, $upper) {
  @if map.has-key(variables.$breakpoints, $lower) and map.has-key(variables.$breakpoints, $upper) {
    $lower-width: functions.calculate-px(map.get(variables.$breakpoints, $lower, width));
    $upper-width: functions.calculate-px(map.get(variables.$breakpoints, $upper, width));

    @media screen and (min-width: $lower-width) and (max-width: $upper-width - 1) {
      @content;
    }
  } @else {
    @error 'Unknown breakpoint range `#{$lower}` - `#{$upper}`. ' + 'Available breakpoints are: #{map.keys(variables.$breakpoints)}.';
  }
}

/// Base style for the layout's outer grid.
/// @access public
/// @group layout
@mixin layout-base {
  display: grid;
  grid-template-areas: '. content .';
}

/// Base style for the layout's inner grid.
/// @access public
/// @group layout
@mixin layout-content {
  display: grid;
  grid-area: content;
  grid-template-columns: repeat(map.get(variables.$breakpoints, large, columns), 1fr);
  grid-template-rows: auto;
  column-gap: var(--pharos-spacing-gutter);
  padding: 0;
  margin: 0;
  list-style: none;

  @include until(medium) {
    grid-template-columns: repeat(map.get(variables.$breakpoints, medium, columns), 1fr);
    grid-template-rows: auto;
  }

  @include until(small) {
    grid-template-columns: repeat(map.get(variables.$breakpoints, small, columns), 1fr);
    grid-template-rows: auto;
  }
}

/// Sets margins for the layout's outer grid.
/// @param {string} $layout - The layout preset
/// @access public
/// @group layout
@mixin layout-base-columns($layout) {
  $breakpoints: map.get(variables.$layouts, $layout);
  $fixed-margin: $layout != '1-col';

  @each $breakpoint, $margin in $breakpoints {
    $i: list.index($breakpoints, $breakpoint $margin);

    @if $i != list.length($breakpoints) {
      $upper: list.nth(map.keys($breakpoints), $i + 1);
      $max-margin: list.nth(map.values($breakpoints), $i + 1);

      @include between($breakpoint, $upper) {
        // prettier doesn't understand the new sass if syntax
        // prettier-ignore
        $layout-margin: if(
          sass($fixed-margin): $margin; else: minmax($margin, $max-margin)
        );

        grid-template-columns: $layout-margin auto $layout-margin;
        grid-template-rows: auto;
      }
    } @else {
      @include at-least($breakpoint) {
        grid-template-columns: minmax($margin, 1fr) auto minmax($margin, 1fr);
        grid-template-rows: auto;
      }
    }
  }
}

/// Sets widths for the layout's inner grid.
/// @param {string} $layout - The layout preset
/// @access public
/// @group layout
@mixin layout-content-width($layout) {
  $breakpoints: map.get(variables.$layouts, $layout);
  $fixed-width: $layout == '1-col';

  // prettier doesn't understand the new sass if syntax
  // prettier-ignore
  $property: if(
    sass($fixed-width): width; else: min-width
  );

  @if $fixed-width {
    @include at-least(medium) {
      margin: auto;
    }
  }

  @each $breakpoint, $margin in $breakpoints {
    $i: list.index($breakpoints, $breakpoint $margin);

    @if $i == 1 {
      @include until($breakpoint) {
        #{$property}: functions.content-width(
          map.get(variables.$breakpoints, $breakpoint, width),
          $margin
        );
      }
    } @else if $i != list.length($breakpoints) {
      $upper: list.nth(map.keys($breakpoints), $i + 1);

      @include between($breakpoint, $upper) {
        #{$property}: functions.content-width(
          map.get(variables.$breakpoints, $breakpoint, width),
          $margin,
          $sidenav: string.index($layout, '--sidenav') and $i > 2
        );
      }
    } @else {
      @include at-least($breakpoint) {
        #{$property}: functions.content-width(
          map.get(variables.$breakpoints, $breakpoint, width),
          $margin,
          $sidenav: string.index($layout, '--sidenav')
        );
      }
    }
  }
}

/// Truncates text based on number of lines.
/// @param {number} $lines [1] - The number of lines allowed before truncating
/// @access public
/// @group typography
@mixin truncate-text($lines: 1) {
  overflow: hidden;
  -webkit-line-clamp: $lines;
  -webkit-box-orient: vertical;
  display: -webkit-box;
  white-space: normal;
}
