<script setup lang="ts">
import { ref, computed, PropType, watch } from 'vue'
import { SizeTypes, MDIconTypes } from '../../types'
import { onClickOutside } from '@vueuse/core'
import CCMDIcon from '../CCMDIcon/CCMDIcon.vue'
import CCCard from '../CCCard/CCCard.vue'
import CCModal from '../CCModal/CCModal.vue'
import { onMounted } from 'vue'

//optionsの型定義
type OptionsObjType = {
  label: string | number
  value: string | number | boolean
  furigana?: string
}

const props = defineProps({
  /**
   * 値
   * @example 'テスト', 100
   */
  modelValue: { type: [String, Number, Boolean] },
  /**
   * オプション
   * @example ['Google', 'Apple', 'Facebook', 'Amazon.com']
   */
  options: { type: Array as PropType<string[] | OptionsObjType[]>, default: [] },
  /**
   * アウトライン
   * @example outline
   */
  outline: { type: Boolean, default: false },
  /**
   * シャドウ
   * @example shadow
   */
  shadow: { type: Boolean, default: false },
  /**
   * スクエア
   * @example square
   */
  square: { type: Boolean, default: false },
  /**
   * オプションズデザイン
   * @example 2
   */
  optionsDesign: {
    type: Number,
    default: 1,
  },
  optionsHeight: { type: Number, default: 300 },
  /**
   * ディセーブル
   * @example disable => true
   */
  disabled: { type: Boolean, default: false },
  /**
   * ラベル
   * @example 'お名前'
   */
  label: { type: String, default: '' },
  /**
   * プレースホルダー
   * @example 'お名前'
   */
  placeholder: { type: String, default: '' },
  /**
   * サイズ
   * @example 'S' | 'M' | 'L'
   */
  size: { type: String as PropType<SizeTypes>, default: 'S' },
  /**
   * 必須テキスト
   * @example '*' | '*必須'
   */
  requiredText: { type: String as PropType<'※' | '※必須'>, default: '' },
  /**
   * 必須アイコン
   * @example true
   */
  requiredIcon: { type: Boolean, default: false },
  /**
   * エラー1
   * @example true
   */
  error1: { type: Boolean, default: false },
  /**
   * エラー2
   * @example true
   */
  error2: { type: Boolean, default: false },
  /**
   * エラー3
   * @example '****を入力してください'
   */
  error3: { type: String, default: '' },
  /**
   * エラー4
   * @example '****を入力してください'
   */
  error4: { type: String, default: '' },
  /**
   * optionsをdialogで表示する
   */
  isDialog: { type: Boolean, default: false },
  /**
   * 右アイコン
   */
  iconRight: { type: String as PropType<MDIconTypes>, default: 'arrow_drop_down' },
  /**
   * value値をclassに追加する
   */
  isAddClassFromValue: { type: Boolean, default: false }
})

/**
 * optionがstringの配列だった場合、オブジェクトの配列に変換する
 */
const optionVal = computed(() => {
  return props.options.map((option: string | OptionsObjType) => {
    if (typeof option === 'string') {
      return { label: option, value: option }
    } else {
      return option
    }
  })
})

const emit = defineEmits<{
  (e: 'update:modelValue', val: string | number | boolean | undefined): void
  (e: 'onBlur'): void
  (e: 'onFocus'): void
}>()

const model = computed({
  get: () => props.modelValue,
  set: (value) => {
    emit('update:modelValue', value)
  },
})

const isDesign = ref<string>()

if (props.outline) {
  isDesign.value = 'outline'
}
if (props.shadow) {
  isDesign.value = 'shadow'
}
if (props.square) {
  isDesign.value = 'square'
}
if (!props.outline && !props.shadow && !props.square) {
  isDesign.value = 'standard'
}

watch(
  () => [props.outline, props.shadow, props.square],
  () => {
    if (props.outline) {
      isDesign.value = 'outline'
    }
    if (props.shadow) {
      isDesign.value = 'shadow'
    }
    if (props.square) {
      isDesign.value = 'square'
    }
    if (!props.outline && !props.shadow && !props.square) {
      isDesign.value = 'standard'
    }
  }
)
watch(
  () => props.options,
  (newOptions) => {
    // furiganaがあるオブジェクト型の配列なら、50音順にソート
    if (
      optionVal.value.length > 0 &&
      typeof optionVal.value[0] === 'object' &&
      'furigana' in optionVal.value[0]
    ) {
      optionVal.value.sort((a, b) => {
        const furiganaA = typeof a === 'object' ? a.furigana ?? String(a.label) : String(a)
        const furiganaB = typeof b === 'object' ? b.furigana ?? String(b.label) : String(b)
        return furiganaA.localeCompare(furiganaB, 'ja')
      })
    }
  },
  { immediate: true }
)

// コンポーネントのrefを定義する
const refBox = ref<HTMLElement>()

// セレクトフォームのrefを定義する
const refSelectFormContainer = ref<HTMLElement>()

// オプションアイテムコンテナのrefを定義する
const refOptionsItemContainer = ref<HTMLElement>()

// optionsの表示状態を定義する
const isOptionsDispState = ref<boolean>(false)

// セレクトフォームをクリックした場合、以下の処理を実行する
const clickSelectForm = () => {
  onFocus()
  if (props.disabled) return

  isOptionsDispState.value = !isOptionsDispState.value

  const labelFlag = ref<boolean>(false)
  if (props.label || props.requiredText || props.requiredIcon) {
    labelFlag.value = true
  }

  // セレクトフォームのrefを変数へ代入する
  const eleSelectFormContainer = refSelectFormContainer.value!

  // セレクトフォームの座標位置topを変数へ代入する
  const eleSelectFormContainerTop = eleSelectFormContainer.getBoundingClientRect().top

  // オプションアイテムコンテナのrefを変数へ代入する
  const eleOptionsItemContainer = refOptionsItemContainer.value!

  // オプションアイテムコンテナのDOM生成ご以下の処理を実行する
  setTimeout(() => {
    // isOptionsDispStateがtrueで、セレクトフォームの座標位置がブラウザの上下中心位置より下の場合
    if (isOptionsDispState.value && eleSelectFormContainerTop > window.innerHeight / 2) {
      eleOptionsItemContainer.style.top = `-${eleOptionsItemContainer.clientHeight - 2}px`
    }

    // isOptionsDispStateがtrueで、props.sizeが'S'で、props.labelまたはprops.requiredTextまたはprops.requiredIconが未設定で、セレクトフォームの座標位置がブラウザの上下中心位置より上の場合
    if (
      isOptionsDispState.value &&
      props.size === 'S' &&
      !labelFlag.value &&
      eleSelectFormContainerTop < window.innerHeight / 2
    ) {
      eleOptionsItemContainer.style.top = '42px'
    }

    // isOptionsDispStateがtrueで、props.sizeが'M'で、props.labelまたはprops.requiredTextまたはprops.requiredIconが未設定で、セレクトフォームの座標位置がブラウザの上下中心位置より上の場合
    if (
      isOptionsDispState.value &&
      props.size === 'M' &&
      !labelFlag.value &&
      eleSelectFormContainerTop < window.innerHeight / 2
    ) {
      eleOptionsItemContainer.style.top = '49px'
    }

    // isOptionsDispStateがtrueで、props.sizeが'L'で、props.labelまたはprops.requiredTextまたはprops.requiredIconが未設定で、セレクトフォームの座標位置がブラウザの上下中心位置より上の場合
    if (
      isOptionsDispState.value &&
      props.size === 'L' &&
      !labelFlag.value &&
      eleSelectFormContainerTop < window.innerHeight / 2
    ) {
      eleOptionsItemContainer.style.top = '54px'
    }

    // isOptionsDispStateがtrueで、props.sizeが'S'で、props.labelまたはprops.requiredTextまたはprops.requiredIconが設定済みで、セレクトフォームの座標位置がブラウザの上下中心位置より上の場合
    if (
      isOptionsDispState.value &&
      props.size === 'S' &&
      labelFlag.value &&
      eleSelectFormContainerTop < window.innerHeight / 2
    ) {
      eleOptionsItemContainer.style.top = '66px'
    }

    // isOptionsDispStateがtrueで、props.sizeが'M'で、props.labelまたはprops.requiredTextまたはprops.requiredIconが設定済みで、セレクトフォームの座標位置がブラウザの上下中心位置より上の場合
    if (
      isOptionsDispState.value &&
      props.size === 'M' &&
      labelFlag.value &&
      eleSelectFormContainerTop < window.innerHeight / 2
    ) {
      eleOptionsItemContainer.style.top = '73px'
    }

    // isOptionsDispStateがtrueで、props.sizeが'L'で、props.labelまたはprops.requiredTextまたはprops.requiredIconが設定済みで、セレクトフォームの座標位置がブラウザの上下中心位置より上の場合
    if (
      isOptionsDispState.value &&
      props.size === 'L' &&
      labelFlag.value &&
      eleSelectFormContainerTop < window.innerHeight / 2
    ) {
      eleOptionsItemContainer.style.top = '78px'
    }
  }, 0)
}

// optionsからアイテムを選択し、v-modelへ値を代入する
const slectOptionsItem = (item: string | number | boolean | undefined) => {
  model.value = optionVal.value.find((val) => val.value === item)?.value
  onBlur()
  isOptionsDispState.value = false
}

// コンポーネントの要素外をクリックした場合、optionsを閉じる
onClickOutside(refBox, () => {
  if (!isOptionsDispState.value) return
  onBlur()
  isOptionsDispState.value = false
})
const onBlur = () => {
  emit('onBlur')
}
const onFocus = () => {
  emit('onFocus')
}
</script>

<template>
  <div ref="refBox" class="_box">
    <div class="_label_container">
      <div class="_label">{{ label }}</div>
      <div
        v-show="requiredText"
        class="_required_text"
        :class="{ _required_position_right: label }"
      >
        {{ requiredText }}
      </div>
      <div
        v-show="requiredIcon"
        class="_required_icon_container"
        :class="{ _required_position_right: label }"
      >
        <div class="_required_icon">必須</div>
      </div>
    </div>

    <div
      ref="refSelectFormContainer"
      class="_select_form_container"
      :class="{
        _size_s: props.size === 'S',
        _size_m: props.size === 'M',
        _size_l: props.size === 'L',
        _standard: isDesign === 'standard',
        _outline: isDesign === 'outline',
        _shadow: isDesign === 'shadow',
        _square: isDesign === 'square',
        _disabled: props.disabled,
        _error_standard:
          isDesign === 'standard' && (props.error1 || props.error2 || props.error3 || props.error4),
        _error_outline_and_square:
          (isDesign === 'outline' || isDesign === 'square') &&
          (props.error1 || props.error2 || props.error3 || props.error4),
        _error_bg_common: props.error2 || props.error4,
      }"
      @click="clickSelectForm"
    >
      <div v-show="!model" class="_placeholder_text">{{ placeholder }}</div>
      <div class="_text" :class="isAddClassFromValue ? `_value-${model}` : ''">{{ optionVal.find((val) => val.value === model)?.label }}</div>
      <CCMDIcon :name="iconRight" class="_arrow_drop_down" />
    </div>

    <div v-show="props.error3" class="_error_message_common">
      {{ props.error3 }}
    </div>

    <div v-show="props.error4" class="_error_message_common">
      {{ props.error4 }}
    </div>

    <CCModal
      v-if="isDialog && isOptionsDispState"
      v-model="isOptionsDispState"
      size="s"
      class="_cc_select_modal"
    >
      <CCCard :style="{ 'max-height': optionsHeight + 'px' }" class="_cc_select_modal_card">
        <div
          v-for="(item, index) in optionVal"
          :key="item.label"
          :class="{
            _options_item_table_design1: props.optionsDesign === 1,
            _options_item_table_select_design1: model === item.value && props.optionsDesign === 1,
            _options_item_table_design2:
              props.optionsDesign === 2 && index !== optionVal.length - 1,
            _options_item_table_last_design2:
              props.optionsDesign === 2 && index === optionVal.length - 1,
            _options_item_table_select_design2: model === item.value && props.optionsDesign === 2,
          }"
          @click="slectOptionsItem(item.value)"
        >
          {{ item.label }}
        </div>
      </CCCard>
    </CCModal>

    <transition>
      <div
        v-show="isOptionsDispState && !isDialog"
        ref="refOptionsItemContainer"
        class="_options_item_container"
        @blur="onBlur()"
      >
        <CCCard :style="{ 'max-height': optionsHeight + 'px', 'overflow': 'auto' }">
          <div
            v-for="(item, index) in optionVal"
            :key="item.label"
            :class="[isAddClassFromValue ? `_value-${item.value}` : '',{
              _options_item_table_design1: props.optionsDesign === 1,
              _options_item_table_select_design1: model === item.value && props.optionsDesign === 1,
              _options_item_table_design2:
                props.optionsDesign === 2 && index !== optionVal.length - 1,
              _options_item_table_last_design2:
                props.optionsDesign === 2 && index === optionVal.length - 1,
              _options_item_table_select_design2: model === item.value && props.optionsDesign === 2,
            }]"
            @click="slectOptionsItem(item.value)"
          >
            {{ item.label }}
          </div>
        </CCCard>
      </div>
    </transition>
  </div>
</template>

<style lang="sass" scoped>
._box
  position: relative
  font-family: 'Roboto', sans-serif
  box-sizing: border-box

._label_container
  display: flex
  align-items: center
  padding: 0 5px
  margin-bottom: 2px
  line-height: 24px

._label
  font-size: 16px
  color: #707070
  width: fit-content

._required_text
  font-size: 14px
  color: #D20000

._required_icon_container
  height: 24px
  display: flex
  align-items: center

._required_icon
  font-size: 14px
  background: #D20000
  color: #fff
  line-height: 22px
  padding: 0 5px
  border-radius: 5px

._required_position_right
  margin-left: auto

._select_form_container
  background-color: #fff
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
  font-size: 16px
  display: flex
  align-items: center
  cursor: pointer
  height: auto

._size_s
  min-height: 40px
  height: auto

._size_m
  min-height: 45px
  height: auto

._size_l
  min-height: 50px
  height: auto

._cc_select_modal_card
  padding: 0
  overflow: auto

._standard
  border-radius: 0px
  padding: 1px 5px 0 16px
  border-bottom: solid 1px var(--c-primary)

._outline
  outline: none
  border-radius: 5px
  padding: 0 5px 0 15px
  border: solid 1px var(--c-primary)

._shadow
  border-radius: 5px
  box-shadow: rgba(99, 99, 99, 0.2) 0px 0px 8px 1px
  -webkit-appearance: none
  appearance: none
  padding: 1px 5px 1px 16px

._square
  border-radius: 0px
  padding: 0 5px 0 15px
  border: solid 1px var(--c-primary)

._disabled
  background: #ECECEC
  cursor: not-allowed
  opacity: 0.6
  ._text
    opacity: 0.7
    white-space: normal
    word-break: break-word
    line-height: 1.4
    display: block

._placeholder_text
  color: #D2D2D2

._arrow_drop_down
  font-size: 30px
  color: #707070
  margin-left: auto

._options_item_container
  position: absolute
  z-index: 999999
  width: 100%
._options_item_container_dialog
  position: fixed
  z-index: 999999999
  width: 100%
  height: 100%
  top: 0
  left: 0
  background: rgba(0, 0, 0, 0.5)
  display: flex
  justify-content: center
  align-items: center
  max-width: 90vh

._options_item_table_design1
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
  padding: 10px
  cursor: pointer
  transition: 0.3s
  color: #707070
._options_item_table_design1:hover
  background: var(--c-primary)
  color: #fff
._options_item_table_select_design1
  background: var(--c-primary)
  color: #fff
._options_item_table_space
  height: 20px

._options_item_table_design2
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
  padding: 10px
  cursor: pointer
  transition: 0.3s
  color: #707070
  border-bottom: 1px solid #707070
._options_item_table_design2:hover
  background: rgb(var(--c-primary-rgb), 0.7)
  color: #fff
._options_item_table_last_design2
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
  padding: 10px
  cursor: pointer
  transition: 0.3s
  color: #707070
._options_item_table_last_design2:hover
  background: rgb(var(--c-primary-rgb), 0.7)
  color: #fff
._options_item_table_select_design2
  background: rgb(var(--c-primary-rgb), 0.7)
  color: #fff

._error_message_common
  color: #D20000
  font-size: 16px

._error_standard
  border-bottom: solid 1px #D20000

._error_outline_and_square
  border: solid 1px #D20000

._error_bg_common
  background: rgba(210,0,0,0.2)

.v-enter-active, .v-leave-active
  transition: opacity 0.3s ease

.v-enter-from, .v-leave-to
  opacity: 0
</style>
