@use 'sass:list';
@use 'sass:math';
@use 'sass:meta';
@use 'sass:selector';

// -----------------------------------------------------------------------------
// Quantity queries
// -----------------------------------------------------------------------------
// Table of contents:
// 1. Last Simple Selector
// 2. Build Quantity Selector
// 3. At least
// 4. At most
// 5. Between
// 6. Exactly
// 7. Even
// 8. Odd

// -----------------------------------------------------------------------------
// 1. Last Simple Selector
// -----------------------------------------------------------------------------

/// Find the last simple selector in a given selector
/// @access private
/// @param  {list | string} $selector - A single selector
/// @return {string}                  - The last simple selector in $selector
/// @example scss
///   $result: _last-simple-selector(ul > li); // li

@function _last-simple-selector($selector) {
  $parsed: selector.parse($selector);

  @if list.length($parsed) > 1 {
    @error '`#{$selector}` contains #{list.length($parsed)} selectors and the `_last-simple-selector()`function accepts only 1.';
  }

  $last-simple-selector: list.nth(list.nth($parsed, 1), -1);

  @return $last-simple-selector;
}

// -----------------------------------------------------------------------------
// 2. Build Quantity Selector
// -----------------------------------------------------------------------------

/// Builds the selector for the quantity query
/// @access private
/// @param  {string} $selector-append      - The selector to be appended
/// @param  {string} $last-selector        - The item's selector
/// @return {list}                         - The final quantity query selector

@function _build-quantity-selector($selector-append, $last-selector) {
  $quantity-selector: ();

  @each $s in & {
    $last-simple-selector: '~'+
      if($last-selector, $last-selector, _last-simple-selector($s));
    $sel: selector.append($s, $selector-append);
    $sel2: selector.nest($sel, $last-simple-selector);
    $quantity-selector: list.append($quantity-selector, $sel, 'comma');
    $quantity-selector: list.append($quantity-selector, $sel2, 'comma');
  }

  @return $quantity-selector;
}

// -----------------------------------------------------------------------------
// 3. At least
// -----------------------------------------------------------------------------

/// Query when total items is at least N items
/// @param  {number} $count - Quantity to match (equal or more)
/// @example scss - Make the items color red when there are 4 items or more
///   ul > li {
///     @include at-least(4) { color: red; }
///   }
/// @example scss - Make the items color blue when there are 6 items or more and use '*' (element agnostic) as the item selector
///   ul > li {
///     @include at-least(6, '*') { color: blue; }
///   }

@mixin at-least($count, $selector: null) {
  $selector-append: ':nth-last-child(n+#{$count})';

  @if meta.type-of($count) !=
    'number' or not
    math.is-unitless($count) or
    $count <
    1
  {
    @error '`#{$count}` is not a valid number for `at-least`';
  }

  @if $selector !=
    null and
    (meta.type-of($selector) != 'string' or list.length($selector) > 1)
  {
    @error '`#{$selector}` is not a valid selector for `at-least`';
  }

  $at-least-selector: _build-quantity-selector($selector-append, $selector);

  @at-root #{$at-least-selector} {
    @content;
  }
}

// -----------------------------------------------------------------------------
// 4. At most
// -----------------------------------------------------------------------------

/// Query when total items is at most N items
/// @param  {number} $count - Quantity to match (equal or less)
/// @example scss - Make the items color red when there are 4 items or less
///   ul > li {
///     @include at-most(4) { color: red; }
///   }
/// @example scss - Make the items color blue when there are 6 items or less and use '*' (element agnostic) as the item selector
///   ul > li {
///     @include at-most(6, '*') { color: blue; }
///   }

@mixin at-most($count, $selector: null) {
  $selector-append: ':nth-last-child(-n+#{$count}):first-child';

  @if meta.type-of($count) !=
    'number' or not
    math.is-unitless($count) or
    $count <
    1
  {
    @error '`#{$count}` is not a valid number for `at-most`.';
  }

  @if $selector !=
    null and
    (meta.type-of($selector) != 'string' or list.length($selector) > 1)
  {
    @error '`#{$selector}` is not a valid selector for `at-most`';
  }

  $at-most-selector: _build-quantity-selector($selector-append, $selector);

  @at-root #{$at-most-selector} {
    @content;
  }
}

// -----------------------------------------------------------------------------
// 5. Between
// -----------------------------------------------------------------------------

/// Query when total items is at least X items and at most Y items
/// @param  {number} $at-least - Lower quantity of items to match
/// @param  {number} $at-most - Higher quantity of items to match
/// @example scss - Make the items color red when there are at least 2 and at most 4 items
///   ul > li {
///     @include between(4, 8) { color: red; }
///   }
/// @example scss - Make the items color blue when there are at least 6 items and at most 10 items and use '*' (element agnostic) as the item selector
///   ul > li {
///     @include between(6, 10, '*') { color: blue; }
///   }

@mixin between($first, $last, $selector: null) {
  $selector-append: ':nth-last-child(n+#{$first}):nth-last-child(-n+#{$last}):first-child';

  @if meta.type-of($first) !=
    'number' or not
    math.is-unitless($first) or
    $first <
    1
  {
    @error '`#{$first}` is not a valid number for `between`';
  }

  @if meta.type-of($last) !=
    'number' or not
    math.is-unitless($last) or
    $last <
    1
  {
    @error '`#{$last}` is not a valid number for `between`';
  }

  @if $first > $last {
    @error '#{$first} can´t be larger that #{$last} for `between`';
  }

  @if $selector !=
    null and
    (meta.type-of($selector) != 'string' or list.length($selector) > 1)
  {
    @error '`#{$selector}` is not a valid selector for `between`';
  }

  $between-selector: _build-quantity-selector($selector-append, $selector);

  @at-root #{$between-selector} {
    @content;
  }
}

// -----------------------------------------------------------------------------
// 6. Exactly
// -----------------------------------------------------------------------------

/// Query when total items is exactly N items
/// @param  {number} $count - Quantity to match (equal)
/// @example scss - Make the items color red when there are exactly 4 items
///   ul > li {
///     @include exactly(4) { color: red; }
///   }
/// @example scss - Make the items color blue when there are exactly 6 items and use '*' (element agnostic) as the item selector
///   ul > li {
///     @include exactly(6, '*') { color: blue; }
///   }

@mixin exactly($count, $selector: null) {
  $selector-append: ':nth-last-child(#{$count}):first-child';

  @if meta.type-of($count) !=
    'number' or not
    math.is-unitless($count) or
    $count <
    1
  {
    @error '`#{$count}` is not a valid number for `exactly`';
  }

  @if $selector !=
    null and
    (meta.type-of($selector) != 'string' or list.length($selector) > 1)
  {
    @error '`#{$selector}` is not a valid selector for `exactly`';
  }

  $exactly-selector: _build-quantity-selector($selector-append, $selector);

  @at-root #{$exactly-selector} {
    @content;
  }
}

// -----------------------------------------------------------------------------
// 7. Even
// -----------------------------------------------------------------------------

/// Query an even number of items
/// @param  {list | string} $selector - A single selector
/// @example scss - Make the items color red when items ar an even number
///   ul > li {
///     @include even() { color: red; }
///   }

@mixin even($selector: null) {
  $selector-append: ':nth-last-child(even):first-child';

  @if $selector !=
    null and
    (meta.type-of($selector) != 'string' or list.length($selector) > 1)
  {
    @error '`#{$selector}` is not a valid selector for `even`';
  }

  $even-selector: _build-quantity-selector($selector-append, $selector);

  @at-root #{$even-selector} {
    @content;
  }
}

// -----------------------------------------------------------------------------
// 8. Odd
// -----------------------------------------------------------------------------

/// Query an odd number of items
/// @param  {list | string} $selector - A single selector
/// @example scss - Make the items color red when items ar an odd number
///   ul > li {
///     @include odd() { color: red; }
///   }

@mixin odd($selector: null) {
  $selector-append: ':nth-last-child(odd):first-child';

  @if $selector !=
    null and
    (meta.type-of($selector) != 'string' or list.length($selector) > 1)
  {
    @error '`#{$selector}` is not a valid selector for `odd`';
  }

  $odd-selector: _build-quantity-selector($selector-append, $selector);

  @at-root #{$odd-selector} {
    @content;
  }
}

// -----------------------------------------------------------------------------
// 9. Multiple of
// -----------------------------------------------------------------------------

/// Query when total items is multiple of N
/// @param  {number} $count - Quantity to match (equal)
/// @example scss - Make the items color red when the total of siblings is multiple of 3 items
///   ul > li {
///     @include multiple-of(3) { color: red; }
///   }
/// @example scss - Make the items color blue when the total of siblings is multiple of 5 items and use '*' (element agnostic) as the item selector
///   ul > li {
///     @include multiple-of(5, '*') { color: blue; }
///   }

@mixin multiple-of($count, $selector: null) {
  $selector-append: ':nth-last-child(#{$count}n):first-child';

  @if meta.type-of($count) !=
    'number' or not
    math.is-unitless($count) or
    $count <
    1
  {
    @error '`#{$count}` is not a valid number for `multiple-of`';
  }

  @if $selector !=
    null and
    (meta.type-of($selector) != 'string' or list.length($selector) > 1)
  {
    @error '`#{$selector}` is not a valid selector for `multiple-of`';
  }

  $multiple-of-selector: _build-quantity-selector($selector-append, $selector);

  @at-root #{$multiple-of-selector} {
    @content;
  }
}
