<template>
  <div
    aria-live="polite"
    :class="[
      `app-alert is-${type}`,
      {
        'is-center': center,
        'is-closable': closable && !isRounded,
        'is-rounded': isRounded,
      },
    ]"
    @close="close"
  >
    <div
      v-if="showIcon || $slots.icon || customIcon"
      class="app-alert-icon"
    >
      <slot name="icon">
        <app-icon
          :icon="customIcon || defaultIcon.icon"
          :size="iconSize"
        />
      </slot>
    </div>
    <div class="app-alert-content">
      <p
        v-if="$slots.title || title"
        class="title"
      >
        <slot name="title">
          {{ title }}
        </slot>
      </p>
      <p v-if="$slots.default || description">
        <slot>
          {{ description }}
        </slot>
      </p>
    </div>
    <AppButton
      v-if="closable && !isRounded"
      ref="closeButton"
      :data-test="dataTestClose"
      :type="defaultIcon.closeType"
      icon="fa-solid fa-xmark"
      size="icon"
      class="close-alert"
      text="close"
      @click="close"
    >
      <span class="sr-only">{{ dataTestClose }}</span>
    </AppButton>
  </div>
</template>

<script>
  /**
   * A standalone global alert that supports branded, danger, success, warning, and info types.
   * Allows for custom icons, description, and title.
   * Can also optionally center, make closable and show icons.
   *
   * @vuedoc
   * @exports AppAlert
   * @category Components
   */
  export default {
    name: 'AppAlert',
    props: {
      /**
       * Used to horizontally align the text in the center.
       */
      center: {
        type: Boolean,
        default: false,
      },
      /**
       * Use to display the icon to close the alert. Not compatible with a true "isRounded" prop.
       */
      closable: {
        type: Boolean,
        default: false,
      },
      /**
       * Use to display a custom icon instead of the default.
       */
      customIcon: {
        type: String,
        default: '',
      },
      /**
       * Used to render the data-test for close button.
       */
      dataTestClose: {
        type: String,
        default: 'close alert',
      },
      /**
       * Used to render the alert copy. This is also the default slot.
       */
      description: {
        type: String,
        default: '',
      },
      /**
       * Used to control size of icon.
       */
      iconSize: {
        type: String,
        default: 'xl',
        validator: (value) => (
          [
            // relative sizes
            '2xs',
            'xs',
            'sm',
            'lg',
            'xl',
            '2xl',
            // literal sizes
            '1x',
            '2x',
            '3x',
            '4x',
            '5x',
            '6x',
            '7x',
            '8x',
            '9x',
            '10x',
          ].includes(value)
        ),
      },
      /**
       * Used to icon associated with the alert type.
       */
      showIcon: {
        type: Boolean,
        default: false,
      },
      /**
       * Convert the alert to a rounded shape.
       */
      isRounded: {
        type: Boolean,
        default: false,
      },
      /**
       * Used to render the alert title above the description.
       * This is also the `title` named slot.
       */
      title: {
        type: String,
        default: '',
      },
      /**
       * Used to style the alert with themes.
       */
      type: {
        type: String,
        default: 'info',
        validator: (value) => (
          ['danger', 'success', 'info', 'warning', 'neutral'].includes(value)
        ),
      },
    },
    computed: {
      /**
       * Determine default icon based on alert type.
       *
       * @returns {string}
       */
      defaultIcon() {
        switch (this.type) {
        case 'success':
          return {
            icon: 'fa-solid fa-check-circle',
            closeType: 'affirm',
          };
        case 'danger':
          return {
            icon: 'fa-solid fa-circle-exclamation',
            closeType: 'decline',
          };
        case 'warning':
          return {
            icon: 'fa-solid fa-triangle-exclamation',
            closeType: null,
          };
        default:
          return {
            icon: 'fa-regular fa-circle-info',
            closeType: null,
          };
        }
      },
    },
    mounted() {
      if (this.$refs.closeButton) {
        this.$nextTick(() => {
          this.$refs.closeButton.$el.focus();
        });
      }
    },
    methods: {
      /**
       * Close alert.
       */
      close() {
        this.$emit('close');
      },
    },
  };
</script>

<style lang="scss" scoped>
  .app-alert {
    position: relative;
    border: 1px solid var(--tf-info);
    background: var(--tf-info-light);
    color: var(--tf-info);
    padding: 10px;
    margin: 0 auto;
    max-width: 100%;
    width: fit-content;
    display: flex;
    align-items: center;
    box-sizing: border-box;
    text-align: left;

    &-content {
      display: flex;
      flex-direction: column;
      flex: 1;
    }

    &-icon {
      padding-right: 10px;
    }

    &.is-center {
      text-align: center;
    }

    &.is-closable {
      padding-right: 30px;
    }

    &.is-rounded {
      border: none;
      border-radius: 100px;
    }

    &.is-warning {
      border-color: var(--tf-warning-border-color);
      color: var(--tf-warning-font-color);
      background: var(--tf-warning-light);
    }

    &.is-success {
      border-color: var(--tf-success-border-color);
      color: var(--tf-success-font-color);
      background: var(--tf-success-light);
    }

    &.is-danger {
      border-color: var(--tf-danger-border-color);
      color: var(--tf-danger-font-color);
      background: var(--tf-danger-light);
    }

    &.is-neutral {
      border-color: var(--tf-gray-dark);
      color: var(--tf-gray-dark);
      background: var(--tf-disabled-light);
    }
  }

  .close-alert {
    position: absolute;
    top: 0;
    right: 0;
  }

  .title {
    font-weight: 600;
  }

  p {
    margin: 3px 0;
  }
</style>

<style lang="scss">
// Left unscoped until move to vue3
// and can use https://vuejs.org/api/sfc-css-features.html#global-selectors

// Links inside alerts should be underlined and inherit certain styles.
// stylelint-disable-next-line
.app-alert {
  .app-button:not(.is-icon) {
    color: inherit !important; // stylelint-disable-line
    text-decoration: underline;
  }

  // Eliminates left and right margins when there are multiple AppButtons so that they can
  // flow normally inline with text inside of alerts.
  .app-button + .app-button {
    margin: 0 !important; // stylelint-disable-line
  }
}
</style>
