////
/// @group card
/// A versatile container for displaying and summarizing individual items, entities, or resources in a visually appealing and concise format
////

@use "sass:map";
@use 'sass:list';

@use "../breakpoint";
@use "../utils";
@use "../selector";
@use "../color";
@use "../layout";

/// Module Settings
/// @type Map
/// @prop {Dimension} padding [2rem] The common padding
/// @prop {Dimension} margin-y [3rem] Top and bottom margin for the card.
/// @prop {Dimension} max-width [28rem] The max-width of the card.
/// @prop {Dimension} line-height [null] The line-height of the card.
/// @prop {Dimension} body-min-height [10rem] the min-height of the card body.
/// @prop {Color} color ["type"] The type color of the card.
/// @prop {Color} color-hover [null] The type color of the card when hovered or focused.
/// @prop {Color} background-color [white] The background color of the card.
/// @prop {Color} background-color-hover [rgb(242, 244, 246)] The background color of the card when hovered or focused.
/// @prop {Dimension} border-radius [5px] The border radius of the card.
/// @prop {CssValue} border-width [1px] The card border width
/// @prop {Color} border-color [#ccc] The card border color
/// @prop {Dimension} border-hover-width [2px] The card border width when hovered or focused.
/// @prop {Color} border-hover-color [#278cca] The card border color when hovered or focused.
/// @prop {CssValue} box-shadow [null] The box-shadow for the card.
/// @prop {CssValue} box-shadow-hover [null] The box-shadow for the card when hovered or focused.
/// @prop {Boolean} box-shadow-interactive-only [false] If true, the box shadow will only be applied to cards that are interactive (clickable).
/// @prop {Boolean} transition-enabled [true] Enable or disable transition for card.
/// @prop {Time} transition-duration [200ms] The animation duration for the card animation.
/// @prop {CssValue} transition-timing-function [ease-in-out] The timing function for the card animation.
/// @prop {List} transition-properties [(border-color, background-color, color, box-shadow, transform, outline-color, outline-width)] The properties for the card animation.
/// @prop {String} clickable-card-selector [data-ulu-proxy-click-init] The selector for proxy-click.js to find the card and implement the clickable card script.
/// @prop {String} clickable-card-interact-selector [&:hover, &:focus-within] The selectors for the cards being interacted with.
/// @prop {Color} title-color [null] The type color of the title.
/// @prop {Color} title-color-hover [null] The type color of the title (if link/button) when hovered or focused
/// @prop {Dimension} title-margin [1rem] The margin for the title.
/// @prop {CssValue} title-font-weight [bold] The font weight for the title.
/// @prop {Boolean} image-within-border [true] If false, the image will bleed to the edges of the card, sitting under the border.
/// @prop {Number} image-aspect-ratio [5/3] The aspect ratio of the image.
/// @prop {Color} image-background-color [rgb(238, 238, 238)] The background color behind the image.
/// @prop {Dimension} image-margin [null] The margin for the image
/// @prop {Dimension} image-border [null] Optional border for the image.
/// @prop {CssValue} image-transform-hover [null] Animation for the image when hovered or focused.
/// @prop {CssValue} image-filter-hover [null] Filter for the image when hovered or focused.
/// @prop {Boolean} image-transition-enabled [true] Enable or disable the image transition.
/// @prop {Time} image-transition-duration [350ms] The duration of the image transition.
/// @prop {CssValue} image-transition-timing-function [ease-in-out] The timing function for the image transition.
/// @prop {List} image-transition-properties [(transform, filter, background-color)] The properties for the image transitions.
/// @prop {Dimension} image-fit-padding [1rem] Padding on inside of image when using image fit modifier
/// @prop {CssValue} image-fit-filter [drop-shadow(0 0px 8px rgba(0, 0, 0, 0.3))] Filter to use on image when using image fit modifier
/// @prop {Dimension} image-icon-max-width [8rem] Max width for image when using the modifier on the .card__image--icon
/// @prop {Dimension} footer-padding-y [0.25rem] The top and bottom padding for the footer.
/// @prop {Dimension} footer-min-height [2.5rem] The min height for the footer
/// @prop {CssValue} footer-justify [flex-end] Flex alignment of footer items (on end by default)
/// @prop {Dimension} footer-inline-padding [0.5rem] The padding for the footer when using the 'footer-inline' modifier.
/// @prop {Color} footer-background-color [null] The background color of the footer.
/// @prop {String} horizontal-enabled [false] Enable the output of horizontal related layout modifiers
/// @prop {String} horizontal-persist-enabled [false] Enable the output of horizontal-persist related layout modifiers
/// @prop {String} horizontal-breakpoint [small] The breakpoint used to change the card to vertical if using the card--horizontal styling. Uses ulu's breakpoint module.
/// @prop {Dimension} horizontal-min-height [20rem] Minimum height when horizontal
/// @prop {Dimension} horizontal-max-width [80rem] Maximum width when horizontal
/// @prop {CssValue} horizontal-image-width [min(33%, 20rem)] The width of the image area when using the 'horizontal' modifier.
/// @prop {Dimension} horizontal-main-max-width [40rem] The max-width of the main content area when using the 'horizontal' modifier.
/// @prop {CssValue} horizontal-aside-width [40%] The width of the aside content area when using the 'horizontal' modifier.
/// @prop {Boolean} aside-rule [true] Whether or not to have a rule separating body and aside
/// @prop {Dimension} aside-rule-width [1px] Size of rule
/// @prop {String} aside-rule-color ["rule-light"] Color of rule
/// @prop {Color} aside-background-color [null] Color of aside background color
/// @prop {Color} overlay-enabled [false] Enable the output of overlay modifier styles
/// @prop {Number} overlay-aspect-ratio [4/3] The aspect ratio of the card when using the 'overlay' modifier.
/// @prop {Color} overlay-color [white] The type color of the card when using card--overlay.
/// @prop {Color} overlay-title-color [null] The color of the title when using the 'overlay' modifier.
/// @prop {Color} overlay-title-color-hover [rgb(79, 175, 230)] The type color of the card title when hovered or focused and when using card--overlay.
/// @prop {Color} overlay-background-color [rgba(0, 0, 0, 0.6)] The background color for the text box when using card--overlay.
/// @prop {Color} overlay-background-color-hover [red] The background color of the overlay when hovered or focused.
/// @prop {Color} overlay-footer-background-color [null] The background color of the footer when using the 'overlay' modifier. Defaults to 'overlay-background-color'.
/// @prop {Boolean} overlay-shading [true] Whether to apply a gradient shading to the overlay to improve text readability.
/// @prop {Dimension} overlay-shading-height [4rem] The height of the gradient shading on the overlay.
/// @prop {Dimension} overlay-body-padding-y [1rem] The top and bottom padding of the body content when using the 'overlay' modifier.

$config: (
  // General
  "padding": 2rem,
  "margin-y": 3rem,
  "max-width": 28rem,
  "line-height" : null,
  "body-min-height": 10rem,
  "color": "type",
  "color-hover": null,
  "background-color": white,
  "background-color-hover": rgb(242, 244, 246),
  "border-radius": 5px,
  "border-width": 1px,
  "border-color": #ccc,
  "border-hover-width": 2px,
  "border-hover-color": #278cca,
  "box-shadow": null,
  "box-shadow-hover": null,
  "box-shadow-interactive-only": false,

  // Transitions & Interactivity
  "transition-enabled": true,
  "transition-duration": 200ms,
  "transition-timing-function": ease-in-out,
  "transition-properties": (border-color, background-color, color, box-shadow, transform, outline-color, outline-width),
  "clickable-card-selector": "[data-ulu-proxy-click-init]",
  "clickable-card-interact-selector": "&:hover, &:focus-within",

  // Title
  "title-color": null,
  "title-color-hover": null,
  "title-margin": 1rem,
  "title-font-weight": bold,

  // Image
  "image-within-border": true,
  "image-aspect-ratio": list.slash(5, 3),
  "image-background-color": rgb(238, 238, 238),
  "image-margin": null,
  "image-border": null, // For when you have a margin
  "image-transform-hover": null,
  "image-filter-hover": null,
  "image-transition-enabled": true,
  "image-transition-duration": 350ms,
  "image-transition-timing-function": ease-in-out,
  "image-transition-properties": (transform, filter, background-color),
  "image-fit-padding": 1rem,
  "image-fit-filter": drop-shadow(0 0px 8px rgba(0, 0, 0, 0.3)),
  "image-icon-max-width": 8rem,

  // Footer
  "footer-padding-y": 0.5rem,
  "footer-min-height": 2.5rem,
  "footer-justify": flex-end,
  "footer-inline-padding": 0.5rem,
  "footer-background-color": null,

  // Horizontal
  "horizontal-enabled" : false,
  "horizontal-persist-enabled" : false,
  "horizontal-breakpoint": "small",
  "horizontal-min-height": 20rem,
  "horizontal-max-width": 80rem,
  "horizontal-image-width": min(33%, 20rem),
  "horizontal-main-max-width": 40rem,
  "horizontal-aside-width": 40%,
  "aside-rule": true,
  "aside-rule-width": 1px,
  "aside-rule-color": "rule-light",
  "aside-background-color": null,

  // Overlay
  "overlay-enabled" : false,
  "overlay-aspect-ratio": list.slash(4, 3),
  "overlay-color": white,
  "overlay-title-color": null,
  "overlay-title-color-hover": rgb(79, 175, 230),
  "overlay-background-color": rgba(0, 0, 0, 0.6),
  "overlay-background-color-hover": rgba(0, 0, 0, 0.8),
  "overlay-footer-background-color": null,
  "overlay-shading": true,
  "overlay-shading-height": 4rem,
  "overlay-body-padding-y": 1rem
) !default;
  
/// Change modules $config
/// @param {Map} $changes Map of changes
/// @example scss
///   @include ulu.component-card-set(( "property" : value ));

@mixin set($changes) {
  $config: map.merge($config, $changes) !global;
}

/// Get a config option
/// @param {Map} $name Name of property
/// @example scss
///   @include ulu.component-card-get(( "property" : value ));

@function get($name) {
  @return utils.require-map-get($config, $name, "card [config]");
}

/// Prints component styles
/// @demo card
/// @example scss
///   @include ulu.component-card-styles();

@mixin styles {
  $prefix: selector.class("card");
  $border-width-negative: -(get("border-width"));

  #{ $prefix } {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr auto;
    
    

    max-width: get("max-width");
    line-height: get("line-height");
    margin-top: get("margin-y");
    margin-bottom: get("margin-y");
    color: color.get(get("color"));
    background-color: color.get(get("background-color"));
    border-radius: get("border-radius");
    border: get("border-width") solid color.get(get("border-color"));
    @if (not get("box-shadow-interactive-only")) {
      box-shadow: get("box-shadow");
    }
    outline: get("border-hover-width") solid transparent;
    outline-offset: $border-width-negative;
  }

  // Remove extra row when image is not present
  // - So footer doesn't fall on the 1fr row
  #{ $prefix }:not(:has(> #{ $prefix }__image)),
  #{ $prefix }--no-image {
    grid-template-rows: 1fr auto;
  }

  @if (get("box-shadow-interactive-only") and get("box-shadow")) {
    @include when-clickable() {
      box-shadow: get("box-shadow");
    }
  }

  @if (get("transition-enabled")) {
    #{ $prefix },
    #{ $prefix }__title {
      transition-duration: get("transition-duration");
      transition-timing-function: get("transition-timing-function");
      transition-property: get("transition-properties");
    }
  }

  @include when-clickable($hover: true) {
    background-color: color.get(get("background-color-hover"));
    color: color.get(get("color-hover"));
    box-shadow: get("box-shadow-hover");
    outline: get("border-hover-width") solid color.get(get("border-hover-color"));
  }

  #{ $prefix }__footer,
  #{ $prefix }__main,
  #{ $prefix }__aside {
    padding: get("padding");
  }
  #{ $prefix }__body {
    grid-column: 1;
    display: flex;
    flex-direction: column;
    min-height: get("body-min-height");
  }
  #{ $prefix }__footer {
    grid-column: 1;
    display: flex;
    align-items: center;
    padding-top: get("footer-padding-y");
    padding-bottom: get("footer-padding-y");
    min-height: get("footer-min-height");
    justify-content: get("footer-justify");
    background-color: color.get(get("footer-background-color"));
    border-bottom-left-radius: get("border-radius");
    border-bottom-right-radius: get("border-radius");
  }
  #{ $prefix }__aside {
    background-color: color.get(get("aside-background-color"));

    @if (get("aside-rule")) {
      // Makes it look like border inside aside padding 
      // without relying on position relative and pseudo technique
      // which affects popovers/etc or other absolute elements inside card
      $aside-rule-color: color.get(get("aside-rule-color"));
      $aside-padding-double: get("padding") * 2;

      background-image: linear-gradient($aside-rule-color, $aside-rule-color);
      background-size: calc(100% - $aside-padding-double) get("aside-rule-width");
      background-position: center top;
      background-repeat: no-repeat;
    }
  }

  #{ $prefix }__image {
    order: -1;
    grid-column: 1 / -1;
    overflow: hidden; // For image corners with border-radius

    @if (not get("image-within-border")) {
      margin: $border-width-negative $border-width-negative 0 $border-width-negative;
    }
    
    background-color: color.get(get("image-background-color"));
    border: get("image-border");
    aspect-ratio: get("image-aspect-ratio");
    @if (get("image-margin")) {
      margin: get("image-margin");
    } @else {
      border-top-right-radius: get("border-radius");
      border-top-left-radius: get("border-radius");
    }
  }

  // Inner Element Styles
  #{ $prefix }__title {
    display: block;
    color: color.get(get("title-color"));
    margin-bottom: get("title-margin");
    font-weight: get("title-font-weight");
  }
  // Only the inner link gets hover state
  #{ $prefix }__title-link {
    all: unset;
    cursor: pointer;
    @if get("title-color-hover") {
      &:hover,
      &:focus {
        color: color.get(get("title-color-hover"));
      }
    }
  }
  // Unless within a clickable card (below)
  @if get("title-color-hover") {
    @include when-clickable($hover: true) {
      #{ $prefix }__title {
        color: color.get(get("title-color-hover"));
      }
    }
  }
  #{ $prefix }__image img,
  #{ $prefix}__image-media {
    width: 100%;
    height: 100%;
    object-fit: cover;
    @if (get("image-transition-enabled")) {
      transition-duration: get("image-transition-duration");
      transition-timing-function: get("image-transition-timing-function");
      transition-property: get("image-transition-properties");
    }
  }
  @if (get("image-transform-hover") or get("image-filter-hover")) {
    @include when-clickable($hover: true) {
      #{ $prefix }__image img,
      #{ $prefix}__image-media {
        transform: get("image-transform-hover");
        filter: get("image-filter-hover");
      }
    }
  }

  // --- Modifiers ---

  // Allows card to fill space (ie. in grid/etc) 
  #{ $prefix }--fill {
    max-width: none;
    height: 100%;
    margin: 0;
  }
  
  #{ $prefix }--footer-inline {
    #{ $prefix }__footer {
      
      padding: get("footer-inline-padding");
      flex-direction: column;
      border-top-right-radius: get("border-radius");
      border-bottom-left-radius: 0;
    }
  }
  #{ $prefix }--footer-inline:not(#{ $prefix }--overlay) {
    // The second column (footer) collapses if unused
    grid-template-columns: 1fr auto;
    #{ $prefix }__footer {
      grid-column: 2;
    }
  }
  #{ $prefix }--footer-start #{ $prefix }__footer {
    justify-content: start;
  }
  #{ $prefix }--footer-center #{ $prefix }__footer {
    justify-content: center;
  }
  #{ $prefix }--footer-end #{ $prefix }__footer {
    justify-content: end;
  }
  

  // Makes image centered with max-width for displaying site icon images
  #{ $prefix }__image--icon {
    display: flex;
    align-items: center;
    justify-content: center;
    img,
    #{ $prefix}__image-media {
      display: block;
      max-width: get("image-icon-max-width");
      height: auto;
    }
  }

  // Makes image fit naturally instead of bleed/cover
  #{ $prefix }--image-fit {
    #{ $prefix }__image {
      img,
      #{ $prefix}__image-media {
        object-fit: contain;
        object-position: top center;
        padding: get("image-fit-padding");
        filter: get("image-fit-filter");
      }
    }
  }
  
  // In case this modifier is being used to hide the image
  // not just to tell the component how to layout without image
  // - Use case is hiding image when you can't control the output 
  //   of the inside of the card (printed no matter what). We had
  //   this happen in HHRC, including this extra CSS for that
  #{ $prefix }--no-image {
    #{ $prefix }__image {
      display: none;
    }
  }

  // Horizontal layout
  @if (get("horizontal-enabled")) {
    @include breakpoint.min(get("horizontal-breakpoint")) {
      #{ $prefix }--horizontal {
        @include -horizontal-styles();
      }
      #{ $prefix }--horizontal-center {
        @include -horizontal-center-styles();
      }
      #{ $prefix }--horizontal#{ $prefix }--footer-inline {
        @include -horizontal-footer-inline-styles();
      }
    }
  }

  // Note: Relying on GZIP and identical code for the duplication below
  // there's really no way to avoid duplication vs complexity for this
  @if (get("horizontal-persist-enabled")) {
    #{ $prefix }--horizontal-persist {
      @include -horizontal-styles();
    }
    #{ $prefix }--horizontal-persist#{ $prefix }--horizontal-center {
      @include -horizontal-center-styles();
    }
    #{ $prefix }--horizontal-persist#{ $prefix }--footer-inline {
      @include -horizontal-footer-inline-styles();
    }
  }

  // Overlay layout (image under content)
  @if (get("overlay-enabled")) {
    #{ $prefix }--overlay {
      // Explanation of aspect-ratio combined with grid-template-rows:
      // - Solves the box growing if needed
      // - First use aspect ration (which grid is based on) but allow the first row 
      // - to grow to min-content if needed (which makes it so the card grows instead 
      // - of overflowing (ie. if you were using "auto auto" or "1fr auto"
      grid-template-rows: minmax(min-content, 1fr) auto;
      aspect-ratio: get("overlay-aspect-ratio");

      #{ $prefix }__body {
        grid-row: 1 / 2;
        align-self: end;
        min-height: 0;
        color: color.get(get("overlay-color"));
        background-color: color.get(get("overlay-background-color"));
        padding-top: get("overlay-body-padding-y");
        padding-bottom: get("overlay-body-padding-y");
        
        @if (get("overlay-shading")) {
          padding-top: calc(get("overlay-body-padding-y") + get("overlay-shading-height"));
          @include -overlay-shading-gradient(
            color.get(get("overlay-background-color"))
          );
        }
        &:not(:has(~ #{ $prefix }__footer)) {
          border-bottom-left-radius: get("border-radius");
          border-bottom-right-radius: get("border-radius");
        }
      }
      #{ $prefix }__aside { 
        background-color: transparent;
      }
      #{ $prefix }__footer {
        grid-row: 2 / 3;
        background-color: color.get(
          utils.fallback(
            color.get(get("overlay-footer-background-color")), 
            color.get(get("overlay-background-color"))
          )
        );
      }
      #{ $prefix }__body,
      #{ $prefix }__footer {
        color: color.get(get("overlay-color"));
        &:last-child {
          border-bottom-left-radius: get("border-radius");
          border-bottom-right-radius: get("border-radius");
        }
      }
      #{ $prefix }__title {
        color: color.get(
          utils.fallback(color.get(get("overlay-title-color")), color.get(get("overlay-color")))
        );
      }
      @if (get("overlay-title-color-hover")) {
        #{ $prefix }__title-link {
          &:hover,
          &:focus {
            color: color.get(get("overlay-title-color-hover"));
          }
        }
      }
      #{ $prefix }__image {
        grid-column: 1 / -1;
        grid-row: 1 / -1;
        border-radius: get("border-radius");
        aspect-ratio: auto;
        img,
        #{ $prefix}__image-media {
          height: 100%;
          object-fit: cover;
          border: 0;
        }
      }
    }

    @include when-clickable($hover: true) {
      &#{ $prefix }--overlay {
        #{ $prefix }__body,
        #{ $prefix }__footer {
          background-color: color.get(get("overlay-background-color-hover"));
        }
        @if (get("overlay-shading")) {
          #{ $prefix }__body {
            @include -overlay-shading-gradient(
              color.get(get("overlay-background-color-hover"))
            );
          }
        }
      }
    }

    #{ $prefix }--overlay#{ $prefix }--footer-inline {
      grid-template-columns: 1fr auto;
      #{ $prefix }__footer {
        grid-column: 2;
        grid-row: 1 / -1;
        border-radius: 0 get("border-radius") get("border-radius")  0;
      }
    }
  }
}

@mixin -horizontal-styles() {
  $prefix: selector.class("card");

  grid-template-columns: get("horizontal-image-width") 1fr;
  // Fix: Use minmax(0, ...) to allow the row to shrink smaller than the image's height
  grid-template-rows: minmax(0, 1fr) auto; // Footer is native height, first row fills
  min-height: get("horizontal-min-height");
  max-width: get("horizontal-max-width");

  // When no footer remove extra row
  &:not(:has(> #{ $prefix }__footer)) {
    grid-template-rows: minmax(0, 1fr);
  }
  #{ $prefix }__image {
    grid-column: 1 / 2;
    grid-row: 1 / -1;
    aspect-ratio: auto;
    // Fix: Force the container to ignore the image's intrinsic height (height: 0)
    // and instead stretch to fill the grid area (min-height: 100%).
    height: 0;
    min-height: 100%;
    border-top-right-radius: 0;
    border-bottom-left-radius: get("border-radius");
  }
  #{ $prefix }__image img,
  #{ $prefix}__image-media {
    // Fix: Ensure the image itself doesn't have a minimum size requirement
    min-height: 0;
  }
  #{ $prefix }__body,
  #{ $prefix }__footer {
    grid-column: 2 / 3;
  }
  #{ $prefix }__footer {
    border-bottom-left-radius: 0;
  }
  #{ $prefix }__body {
    flex-direction: row;
    justify-content: space-between;
  }
  #{ $prefix }__main {
    flex-grow: 1;
    max-width: get("horizontal-main-max-width");
  }
  #{ $prefix }__aside {
    flex: 0 0 get("horizontal-aside-width");
    height: 100%;
    @if (get("aside-rule")) {
      $aside-padding-double: get("padding") * 2;
      background-size: get("aside-rule-width") calc(100% - $aside-padding-double);
      background-position: left center;
    }
  }

  // For modern browsers
  // Optionally use no-image modifier for older browser support
  &:not(:has(#{ $prefix }__image)) {
    @include -card-horizontal-no-image-styles();
  }
  &#{ $prefix }--no-image {
    @include -card-horizontal-no-image-styles();
  }
  
}

@mixin -horizontal-center-styles() {
  $prefix: selector.class("card");

  #{ $prefix }__body {
    align-self: center;
  }
}

@mixin -horizontal-footer-inline-styles() {
  $prefix: selector.class("card");

  grid-template-columns: get("horizontal-image-width") 1fr auto;
  // Fix: Use minmax(0, ...) to allow the row to shrink smaller than the image's height
  grid-template-rows: minmax(0, 1fr); 
  #{ $prefix }__footer {
    grid-column: 3;
  }
}


/// Applies styles to cards that are designated as 'clickable'. This can be for the resting state or for interaction states like hover and focus.
/// @param {Boolean} $hover [false] Apply styles when the card is being hover/focused within, else applies styles to rest state of a clickable card (one who has a proxy click setup)
/// @param {String} $extra-selector Selector to be appended to the list
/// @example scss
///   @include ulu.component-card-when-clickable($hover: true) {
///     background-color: lightblue;
///   }

@mixin when-clickable($hover: false, $extra-selector: null) {
  $prefix: selector.class("card");
  $selectors: (
    #{ $prefix }#{ get("clickable-card-selector") },
    a#{ $prefix },
    button#{ $prefix },
    #{ $prefix }--clickable
  );

  @if $extra-selector {
    $selectors: list.append($selectors, $extra-selector, $separator: comma);
  }

  #{$selectors} {
    @if ($hover) {
      #{ get("clickable-card-interact-selector") } {
        @content;
      }
    } @else {
      @content;
    }
  }
}

// Internal mixins
@mixin -card-horizontal-no-image-styles() {
  $prefix: selector.class("card");
  grid-template-columns: 1fr;
  #{ $prefix }__body,
  #{ $prefix }__footer {
    grid-column: 1 / 2;
  }
}
@mixin -overlay-shading-gradient($background-color) {
  background: linear-gradient(
    to bottom, 
    transparent 0%, 
    $background-color get("overlay-shading-height"), 
    $background-color 100%
  );
}