@use "sass:math";
@use "@uqds/core/src/scss/global" as core;

// Grid schema
// name: (columns, gutter, margin)
$schema: (
  "base": (
    6,
    core.$space-m,
    core.$space-l,
  ),
  "md": (
    8,
    core.$space-l,
    core.$space-ul,
  ),
  "xl": (
    12,
    core.$space-xl,
    9.375rem,
  ),
) !default;

$schema-base: map-get(
  $map: $schema,
  $key: "base",
) !default;
$schema-md: map-get(
  $map: $schema,
  $key: "md",
) !default;
$schema-xl: map-get(
  $map: $schema,
  $key: "xl",
) !default;

$shadow-column-count: 24 !default;
$max-container-width: 71.25rem !default;

/**
 * Layout containment (breakpoint driven)
 */
@mixin layout-containment {
  margin-left: nth($schema-base, 3) - nth($schema-base, 2) * 0.5;
  margin-right: nth($schema-base, 3) - nth($schema-base, 2) * 0.5;

  // TODO: if we can perform the following checks on the parent element width
  // instead of the viewport width, we can achieve a more friendlier to use
  // implementation of the layout system (albeit less strict). Also, it means
  // that the columns get enumerated based on available space when rows are
  // nested.

  // Ease into larger margin
  @media (min-width: #{core.$screen-md + (2 * nth($schema-base, 3)) - (2 * nth($schema-md, 3))}) {
    margin-left: core.$space-auto;
    margin-right: core.$space-auto;
    max-width: core.$screen-md - (2 * nth($schema-md, 3)) + nth($schema-base, 2);
  }

  @media #{core.$screen-md-up} {
    margin-left: nth($schema-md, 3) - nth($schema-md, 2) * 0.5;
    margin-right: nth($schema-md, 3) - nth($schema-md, 2) * 0.5;
    max-width: none;
  }

  // Ease into larger margin
  @media (min-width: #{core.$screen-xl + (2 * nth($schema-md, 3)) - (2 * nth($schema-xl, 3))}) {
    margin-left: core.$space-auto;
    margin-right: core.$space-auto;
    max-width: core.$screen-xl - 2 * nth($schema-xl, 3) + nth($schema-md, 2);
  }

  @media (min-width: #{core.$screen-xl}) {
    margin-left: nth($schema-xl, 3) - nth($schema-xl, 2) * 0.5;
    margin-right: nth($schema-xl, 3) - nth($schema-xl, 2) * 0.5;
    max-width: none;
  }

  // Terminal
  @media (min-width: #{$max-container-width + (2 * nth($schema-xl, 3))}) {
    margin-left: core.$space-auto;
    margin-right: core.$space-auto;
    max-width: $max-container-width + nth($schema-xl, 2);
  }
}

/**
 * For nested containers (reverses layout-containment)
 */
@mixin layout-nested {
  margin-left: -(nth($schema-base, 2) * 0.5);
  margin-right: -(nth($schema-base, 2) * 0.5);
  max-width: none;

  @media #{core.$screen-md-up} {
    margin-left: -(nth($schema-md, 2) * 0.5);
    margin-right: -(nth($schema-md, 2) * 0.5);
  }

  @media #{core.$screen-xl-up} {
    margin-left: -(nth($schema-xl, 2) * 0.5);
    margin-right: -(nth($schema-xl, 2) * 0.5);
  }
}

/**
 * For full-width containers (adds half gutter width to horizontal margins)
 */
@mixin layout-full-width {
  @include layout-nested();
}

/*
 * Convert fraction to percentage
 */
@function _grid-fraction-to-percent(
  $columns,
  $column-count: nth($schema-base, 1)
) {
  @return percentage(math.div($columns, $column-count));
}

/* Padding to make up gaps/gutters (horizonal) */
@mixin grid-col-padding {
  padding-left: nth($schema-base, 2) * 0.5;
  padding-right: nth($schema-base, 2) * 0.5;

  @media #{core.$screen-md-up} {
    padding-left: nth($schema-md, 2) * 0.5;
    padding-right: nth($schema-md, 2) * 0.5;
  }

  @media #{core.$screen-xl-up} {
    padding-left: nth($schema-xl, 2) * 0.5;
    padding-right: nth($schema-xl, 2) * 0.5;
  }
}

/**
 * Map columns to another base
 */
@function calc-true-colspan($span, $column-count, $shadow-column-count) {
  @return round(math.div($shadow-column-count, $column-count) * $span);
}

/**
 * Col span classes
 */
@mixin grid-col-span-classes(
  $column-count: nth($schema-base, 1),
  $breakpoint-name: null,
  $shadow-column-count: $shadow-column-count
) {
  $col-span-mod-prefix: "";

  @if ($breakpoint-name) {
    $col-span-mod-prefix: "#{$breakpoint-name}-";
  }

  .uq-grid__col--#{$col-span-mod-prefix} {
    @for $span from 1 through $column-count {
      &#{$span} {
        @include grid-col-width($span, $column-count);
      }
    }
  }

  @supports (display: grid) {
    .uq-grid__col--#{$col-span-mod-prefix} {
      @for $span from 1 through $column-count {
        &#{$span} {
          grid-column: auto /
            span
            calc-true-colspan($span, $column-count, $shadow-column-count);
          width: auto; // required to enable a flexbox fallback
        }
      }
    }
  }
}

/**
 * Generate grid classes (CSS Grid implementation)
 */
@mixin grid-row(
  $column-count: nth($schema-base, 1),
  $breakpoint-name: null,
  $shadow-column-count: $shadow-column-count
) {
  .uq-grid {
    box-sizing: border-box;

    *,
    *::before,
    *::after {
      box-sizing: border-box;
    }
  }

  // Flex fallback implementation
  @include _grid-row--flex($column-count, $breakpoint-name);

  // Start CSS Grid implementation
  @supports (display: grid) {
    .uq-grid {
      display: grid;
      grid-template-columns: repeat($shadow-column-count, 1fr);
      // Using the CSS Grid gap/grid-gap property is causing overflow problems
      // with small screens and nested grids. We are using padding to implement
      // horizontal gutters instead.
      grid-gap: nth($schema-base, 2) 0;
      margin-top: 0; // required to enable a flexbox fallback
      margin-bottom: 0; // required to enable a flexbox fallback

      @media #{core.$screen-md-up} {
        grid-row-gap: nth($schema-md, 2);
        margin-top: 0; // required to enable a flexbox fallback
        margin-bottom: 0; // required to enable a flexbox fallback
      }

      @media #{core.$screen-xl-up} {
        grid-row-gap: nth($schema-xl, 2);
        margin-top: 0; // required to enable a flexbox fallback
        margin-bottom: 0; // required to enable a flexbox fallback
      }

      &__col {
        padding-top: 0; // required to enable a flexbox fallback
        padding-bottom: 0; // required to enable a flexbox fallback

        @media #{core.$screen-md-up} {
          padding-top: 0; // required to enable a flexbox fallback
          padding-bottom: 0; // required to enable a flexbox fallback
        }
        @media #{core.$screen-xl-up} {
          padding-top: 0; // required to enable a flexbox fallback
          padding-bottom: 0; // required to enable a flexbox fallback
        }
      }
    }
  }

  .uq-grid {
    @content;
  }

  @include grid-col-span-classes(
    $column-count,
    $breakpoint-name,
    $shadow-column-count
  );
}

/**
 * Row helpers for flex
 */
@mixin grid-row-flex-base {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-items: stretch;
  align-content: flex-start;
}

/**
 * Generate Flex fallback grid classes
 */
@mixin _grid-row--flex(
  $column-count: nth($schema-base, 1),
  $breakpoint-name: null
) {
  $col-span-mod-prefix: "";

  @if ($breakpoint-name) {
    $col-span-mod-prefix: "#{$breakpoint-name}-";
  }

  .uq-grid {
    @include grid-row-flex-base;
    @include _grid-row-v-margins;

    @content;

    &__col {
      /*
        TODO: implement row gap (at the moment we can't test—using @supports—for
        partial support i.e. we can't test for `gap` property support in flex
        layouts even though it's specified)
      */
      @include grid-col-padding;
      @include _grid-col-v-padding;
      flex: none;
    }
  }
}

/**
 * Column helpers for flex
 */
@mixin grid-col-width($columns, $column-count: nth($schema-base, 1)) {
  width: _grid-fraction-to-percent($columns, $column-count);
}

@mixin grid-col-offset($columns, $column-count: nth($schema-base, 1)) {
  margin-left: _grid-fraction-to-percent($columns, $column-count);
}

/* Padding to make up gaps/gutters (vertical) */
@mixin _grid-col-v-padding {
  padding-top: nth($schema-base, 2) * 0.5;
  padding-bottom: nth($schema-base, 2) * 0.5;

  @media #{core.$screen-md-up} {
    padding-top: nth($schema-md, 2) * 0.5;
    padding-bottom: nth($schema-md, 2) * 0.5;
  }

  @media #{core.$screen-xl-up} {
    padding-top: nth($schema-xl, 2) * 0.5;
    padding-bottom: nth($schema-xl, 2) * 0.5;
  }
}

/* Margins to normalise the gaps/gutter offset (vertical) */
@mixin _grid-row-v-margins {
  margin-top: -(nth($schema-base, 2) * 0.5);
  margin-bottom: -(nth($schema-base, 2) * 0.5);

  @media #{core.$screen-md-up} {
    margin-top: -(nth($schema-md, 2) * 0.5);
    margin-bottom: -(nth($schema-md, 2) * 0.5);
  }

  @media #{core.$screen-xl-up} {
    margin-top: -(nth($schema-xl, 2) * 0.5);
    margin-bottom: -(nth($schema-xl, 2) * 0.5);
  }
}
