<template>
  <div v-click-outside="closeDropdown" class="cpSelectMenu">
    <button class="cpSelectMenu__button" type="button" @click="toggleDropdown">
      <span class="cpSelectMenu__label">{{ selectedValue.label }}</span>
      <cp-icon
        class="cpSelectMenu__icon"
        :class="{ 'cpSelectMenu__icon--isOpened': isDropdownOpen }"
        type="chevron-down"
      />
    </button>
    <transition mode="out-in" name="scale-elastic">
      <div v-if="isDropdownOpen" ref="dropdownRef" class="cpSelectMenu__dropdown dropdown">
        <p v-if="dropdownTitle" class="dropdown__title">
          {{ dropdownTitle }}
          <button type="button" @click="closeDropdown"><cp-icon type="x" /></button>
        </p>
        <div class="dropdown__list">
          <label v-if="hasFilter" class="dropdown__filterBar">
            <cp-icon type="search" />
            <input
              ref="dropdownFilterRef"
              :placeholder="dropdownFilterPlaceholder"
              type="text"
              @input="updateFilterValue"
            />
          </label>
          <ul v-if="values.length" class="dropdown__items">
            <li v-for="({ value, label }, index) in values" :key="index">
              <label class="dropdown__item" :for="value">
                <input
                  :id="value"
                  :checked="isSelectedValue(value)"
                  :type="inputType"
                  :value="value"
                  @change="onChange({ value, label })"
                />
                <cp-icon v-if="isSelectedValue(value)" class="dropdown__icon" type="check" />
                {{ label }}
              </label>
            </li>
          </ul>
          <p v-else class="dropdown__emptyState">{{ dropdownEmptyViewPlaceholder }}</p>
        </div>
      </div>
    </transition>
  </div>
</template>

<script setup lang="ts">
import { computed, nextTick, ref } from 'vue'

export interface SelectValue {
  label: string
  value: string
}

interface Props {
  closeOnSelect?: boolean
  dropdownEmptyViewPlaceholder?: string
  dropdownFilterPlaceholder?: string
  dropdownTitle?: string
  hasFilter?: boolean
  isMultiSelect?: boolean
  selectedValue: SelectValue
  values: SelectValue[]
}

interface Emits {
  (e: 'update:selectedValue', value: SelectValue): void
  (e: 'onClose'): void
  (e: 'onFilterChange', value: string): void
}

const props = withDefaults(defineProps<Props>(), {
  isMultiSelect: false,
  hasFilter: false,
  dropdownTitle: 'Select value',
  dropdownFilterPlaceholder: 'Filter...',
  dropdownEmptyViewPlaceholder: 'No option found',
  closeOnSelect: true,
})

const emit = defineEmits<Emits>()

const isDropdownOpen = ref(false)
const dropdownRef = ref<HTMLElement>()
const dropdownFilterRef = ref<HTMLInputElement>()

const inputType = computed(() => {
  return props.isMultiSelect ? 'checkbox' : 'radio'
})

const toggleDropdown = (): void => {
  isDropdownOpen.value = !isDropdownOpen.value

  if (isDropdownOpen.value && props.hasFilter) {
    nextTick(() => {
      if (dropdownFilterRef.value) {
        dropdownFilterRef.value.focus()
      }
    })
  }
}

const closeDropdown = (): void => {
  if (!isDropdownOpen.value) {
    return
  }

  isDropdownOpen.value = false
  resetInputValue()
  emit('onClose')
}

const onChange = (value: SelectValue): void => {
  if (value.value !== props.selectedValue.value) {
    emit('update:selectedValue', value)
  }
  if (props.closeOnSelect) {
    closeDropdown()
  }
}

const updateFilterValue = (event: Event): void => {
  const target = event.target as HTMLInputElement
  emit('onFilterChange', target.value)
}

const resetInputValue = (): void => {
  emit('onFilterChange', '')
}

const isSelectedValue = (value: string): boolean => {
  return value === props.selectedValue.value
}
</script>

<style lang="scss">
.cpSelectMenu {
  position: relative;
  font-size: var(--cp-text-size-sm);

  &__button {
    display: flex;
    width: 100%;
    align-items: center;
    justify-content: space-between;
    padding: var(--cp-spacing-md);
    border: none;
    border-radius: var(--cp-radius-md-lg);
    background: var(--cp-background-white);
    box-shadow: inset 0 var(--cp-dimensions-0_25) var(--cp-dimensions-0_5) rgba(0, 0, 0, 12%);
    font-size: inherit;
    font-weight: normal;
    line-height: var(--cp-line-height-md);
    outline: var(--cp-dimensions-0_25) solid var(--cp-border-soft);
    text-transform: capitalize;

    &:hover,
    &:active,
    &:focus {
      outline: var(--cp-dimensions-0_25) solid var(--cp-background-accent-solid);
    }

    &:focus {
      box-shadow: 0 0 0 var(--cp-dimensions-0_5) var(--cp-background-accent-solid);
      outline: var(--cp-dimensions-0_5) solid var(--cp-background-accent-solid);
    }
  }

  &__label {
    @extend %u-text-ellipsis;
  }

  &__icon {
    width: var(--cp-dimensions-5);
    height: var(--cp-dimensions-5);
    flex-shrink: 0;
    margin-left: var(--cp-dimensions-3);
    transition: transform 0.2s ease-out;

    &--isOpened {
      transform: rotateX(180deg);
    }
  }

  &__dropdown {
    position: absolute;
    z-index: 3;
    top: calc(100% + var(--cp-dimensions-1));
    left: 0;
    width: max(100%, calc(var(--cp-dimensions-1) * 62.5));
    border: var(--cp-dimensions-0_25) solid var(--cp-border-soft);
    border-radius: var(--cp-radius-md-lg);
    background: var(--cp-background-white);
    font-size: inherit;
  }

  .dropdown {
    svg {
      width: calc(var(--cp-dimensions-1) * 4.5);
      height: calc(var(--cp-dimensions-1) * 4.5);
    }

    &__title {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: var(--cp-spacing-lg) var(--cp-spacing-md);
      color: var(--cp-text-primary);
      font-weight: 600;
      white-space: normal;
    }

    &__filterBar {
      display: flex;
      align-items: center;
      padding: var(--cp-spacing-md);
      border-top: var(--cp-dimensions-0_25) solid var(--cp-border-soft);
      border-bottom: var(--cp-dimensions-0_25) solid var(--cp-border-soft);
      background-color: var(--cp-background-disabled);

      & > input {
        flex: 1;
        background-color: transparent;
        color: var(--cp-text-primary);

        // stylelint-disable-next-line max-nesting-depth
        &::placeholder {
          color: var(--cp-text-secondary);
        }
      }

      i {
        flex-shrink: 0;
        margin-right: var(--cp-spacing-md);
        color: var(--cp-text-primary);
      }
    }

    &__list {
      display: flex;
      min-height: calc(var(--cp-dimensions-1) * 62.5);
      flex-direction: column;
    }

    &__items {
      max-height: calc(var(--cp-dimensions-1) * 62.5);
      padding: 0;
      margin: var(--cp-spacing-sm) 0;
      overflow-y: auto;

      li {
        list-style: none;
      }
    }

    &__item {
      position: relative;
      display: flex;
      width: 100%;
      padding: var(--cp-spacing-md) var(--cp-spacing-xl) var(--cp-spacing-md) calc(var(--cp-dimensions-1) * 9);
      cursor: pointer;
      text-align: initial;
      text-transform: capitalize;

      &:hover {
        background-color: var(--cp-background-disabled);
      }

      input {
        display: none;
      }
    }

    &__icon {
      position: absolute;
      top: 50%;
      left: var(--cp-spacing-md);
      color: var(--cp-background-accent-solid);
      transform: translateY(-50%);
    }

    &__emptyState {
      display: flex;
      flex: 1;
      align-items: center;
      justify-content: center;
      padding: var(--cp-spacing-xl) var(--cp-spacing-md);
      color: var(--cp-text-secondary);
    }
  }
}
</style>
