<script setup lang="ts" generic="TableType extends Record<string, any>">
import { ref, onMounted, Ref, computed, watch } from 'vue'
import { CCCard, CCMDIcon, CCInput, CCSelect, CCButton, CCCheckbox, CCRadio } from '../../index'
import {
  DrawerFilterType,
  InputFilterType,
  SelectFilterType,
  CalendarFilterType,
  CheckboxFilterType,
  RadioFilterType,
} from '../../types/Filter'
import { WhereClauseTuple } from '@magnetarjs/types'

const props = defineProps({
  /**
   * ドロワー内に表示させる項目
   */
  modelValue: {
    type: Array as () => DrawerFilterType<TableType>[],
    default: () => [],
  },
})

const emit = defineEmits<{
  (e: 'filter', value: boolean): void
  (e: 'update:modelValue', value: DrawerFilterType<TableType>[]): void
}>()

/** ページ側から受け取ったテーブル情報 */
const tableColumnObj = computed({
  get: () => props.modelValue,
  set: (value) => {
    emit('update:modelValue', value)
  },
})

/** 詳細検索メニュー表示非常時のフラグ */
const isSearchMenu = ref(false)

/** tableColumnObjのtypeプロパティがinputであるかどうか */
const isInputType = ref(false)
/** typeプロパティがinputのオブジェクトを格納 */
const inputObjArray: Ref<InputFilterType<TableType>[]> = ref([])
/** 各インプットフォームで入力された値を格納 */
const inputValueArray = ref<string[]>([])

/** tableColumnObjのtypeプロパティがselectであるかどうか */
const isSelectType = ref(false)
/** typeプロパティがselectのオブジェクトを格納 */
const selectObjArray: Ref<SelectFilterType<TableType>[]> = ref([])
/** 各ドロップダウンの選択肢を格納 */
const selectOptionsArray = ref<string[][]>([])
/** 各ドロップダウンで選択された値を格納 */
const selectValueArray = ref<string[]>([])

/** tableColumnObjのtypeプロパティがcheckboxesであるかどうか */
const isCheckboxesType = ref(false)
/** typeプロパティがcheckboxesのオブジェクトを格納 */
const checkboxesObjArray: Ref<CheckboxFilterType<TableType>[]> = ref([])

/** 各チェックボックスの選択肢を格納 */
const checkboxesOptionsArray = ref<string[][]>([])
/** 各チェックボックスの真偽値を格納 */
const checkboxesValueArray = ref<boolean[][]>([])

/** tableColumnObjにradioのtypeプロパティがあるかのフラグ */
const isRadioType = ref(false)
/** typeプロパティがradioのオブジェクトを格納 */
const radioObjArray: Ref<RadioFilterType<TableType>[]> = ref([])
/** 各ラジオボタンの選択肢を格納 */
const radioOptionsArray = ref<string[][]>([])
/** 各ラジオボタンで選択された値を格納 */
const radioValueArray = ref<string[]>([])

/** tableColumnObjのtypeプロパティがcalendarであるかどうか */
const isCalendarType = ref(false)
/** typeプロパティがcalendarのオブジェクトを格納 */
const calendarObjArray: Ref<CalendarFilterType<TableType>[]> = ref([])
/** 各カレンダーの開始日を格納 */
const calendarStartArray = ref<string[]>([])
/** 各カレンダーの終了日を格納 */
const calendarEndArray = ref<string[]>([])

onMounted(() => {
  // 各オブジェクトのtypeプロパティを確認してフラグを切り替え
  tableColumnObj.value.forEach((column) => {
    if (column.filterType === 'input') isInputType.value = true
    if (column.filterType === 'select') isSelectType.value = true
    if (column.filterType === 'checkboxes') isCheckboxesType.value = true
    if (column.filterType === 'radio') isRadioType.value = true
    if (column.filterType === 'calendar') isCalendarType.value = true
  })

  // tableColumnObjにinputのtypeプロパティがあった場合
  if (isInputType.value) {
    inputObjArray.value = tableColumnObj.value.filter(
      (column) => column.filterType === 'input'
    ) as InputFilterType<TableType>[]
  }

  // tableColumnObjにselectのtypeプロパティがあった場合
  if (isSelectType.value) {
    selectObjArray.value = tableColumnObj.value.filter(
      (column) => column.filterType === 'select'
    ) as SelectFilterType<TableType>[]
    // 各ドロップダウンの選択肢を格納
    const selectOption = selectObjArray.value.map((column) => column.options ?? [])
    selectOptionsArray.value.push(...selectOption)
  }

  // tableColumnObjにcheckboxesのtypeプロパティがあった場合
  if (isCheckboxesType.value) {
    checkboxesObjArray.value = tableColumnObj.value.filter(
      (column) => column.filterType === 'checkboxes'
    ) as CheckboxFilterType<TableType>[]
    // 各チェックボックスの選択肢を格納
    const checkboxesOption = checkboxesObjArray.value.map((column) => column.options ?? [])
    checkboxesOptionsArray.value.push(...checkboxesOption)
    // 各チェックボックスの初期値をfalseに設定
    checkboxesObjArray.value.forEach((column, index) => {
      checkboxesValueArray.value[index] = (column?.options ?? []).map(() => false)
    })
  }

  // tableColumnObjにradioのtypeプロパティがあった場合
  if (isRadioType.value) {
    radioObjArray.value = tableColumnObj.value.filter(
      (column) => column.filterType === 'radio'
    ) as RadioFilterType<TableType>[]
    // 各ラジオボタンの選択肢を格納
    const radioOption = radioObjArray.value.map((column) => column.options ?? [])
    radioOptionsArray.value.push(...radioOption)
  }

  // tableColumnObjにcalendarのtypeプロパティがあった場合
  if (isCalendarType.value) {
    calendarObjArray.value = tableColumnObj.value.filter(
      (column) => column.filterType === 'calendar'
    ) as CalendarFilterType<TableType>[]
  }

  // 詳細検索メニューの外側をクリックした時にメニューを閉じる
  document.addEventListener('click', (e) => {
    if (!(e.target as Element).closest('#menuArea') && isSearchMenu.value) searchMenuClose()
  })
  allClear()
})

/**
 * インプットフォームに入力された全角英数字、半角カタカナを変換
 * @param value 入力された値
 * @returns 変換済みの値
 */
const inputValueConvert = (value: string) => {
  // 全角英数字を半角英数字に変換
  let convertedValue = value.replace(/[０-９Ａ-Ｚａ-ｚ]/g, (s) =>
    String.fromCharCode(s.charCodeAt(0) - 0xfee0)
  )
  // 半角カタカナを全角カタカナに変換
  convertedValue = convertedValue.normalize('NFKC')
  return convertedValue
}

// インプットフォームに入力された値を監視して、tableColumnObjのsearchプロパティに反映
watch(
  inputValueArray,
  (newValue) => {
    inputObjArray.value.forEach((column, index) => {
      const inputValue = newValue[index] || ''
      const convertedValue = inputValueConvert(inputValue)
      column.search = convertedValue as WhereClauseTuple<TableType>[2]
    })
  },
  { deep: true }
)

// ドロップダウンで選択された値を監視して、tableColumnObjのsearchプロパティに反映
watch(
  selectValueArray,
  (newValue) => {
    selectObjArray.value.forEach((column, index) => {
      column.search = (newValue[index] || '') as WhereClauseTuple<TableType>[2]
    })
  },
  { deep: true }
)

// チェックボックスで選択された値を監視して、tableColumnObjのsearchプロパティに反映
watch(
  checkboxesValueArray,
  (newValue) => {
    const checkboxesActive = checkboxesOptionsArray.value.map((column, index) =>
      column.filter((_, subIndex) => newValue[index][subIndex])
    )
    checkboxesObjArray.value.forEach((column, index) => {
      column.search = (checkboxesActive[index] || []) as WhereClauseTuple<TableType>[2]
    })
  },
  { deep: true }
)

// ラジオボタンで選択された値を監視して、tableColumnObjのsearchプロパティに反映
watch(
  radioValueArray,
  (newValue) => {
    radioObjArray.value.forEach((column, index) => {
      column.search = (newValue[index] || '') as WhereClauseTuple<TableType>[2]
    })
  },
  { deep: true }
)

// カレンダーで選択された値を監視して、tableColumnObjのsearchプロパティに反映
watch(
  [calendarStartArray, calendarEndArray],
  ([startValue, endValue]) => {
    calendarObjArray.value.forEach((column, index) => {
      column.search = { start: startValue[index] || '', end: endValue[index] || '' }
    })
  },
  { deep: true }
)

/**
 * 詳細検索メニューを開く
 */
const searchMenuOpen = (event: MouseEvent) => {
  event.stopPropagation()
  isSearchMenu.value = true
}

/**
 * 詳細検索メニューを閉じる
 */
const searchMenuClose = () => {
  isSearchMenu.value = false
}

/**
 * 各項目で入力された値をクリアする
 * @param type input | select | checkboxes | radio | calendar
 * @param index インデックス
 */
const valueClear = (
  type: 'input' | 'select' | 'checkboxes' | 'radio' | 'calendar',
  index: number
) => {
  if (type === 'input') inputValueArray.value[index] = ''
  if (type === 'select') selectValueArray.value[index] = ''
  if (type === 'checkboxes') checkboxesValueArray.value[index].fill(false)
  if (type === 'radio') radioValueArray.value[index] = ''
  if (type === 'calendar') {
    calendarStartArray.value = new Array(calendarObjArray.value.length).fill('')
    calendarEndArray.value = new Array(calendarObjArray.value.length).fill('')
  }
}

/**
 * 各項目で入力されたすべての値をクリアしてページ側に伝える
 */
const allClear = () => {
  calendarStartArray.value = new Array(calendarObjArray.value.length).fill('')
  calendarEndArray.value = new Array(calendarObjArray.value.length).fill('')
  selectValueArray.value = []
  checkboxesValueArray.value.forEach((column) => {
    column.fill(false)
  })
  radioValueArray.value = []
  inputValueArray.value = []
  emit('filter', false)
}

/**
 * 検索実行をページ側に伝える
 */
const runSearch = () => {
  // カレンダーにエラーが発生していないかを確認
  const hasError = calendarStartArray.value.some((start, index) => {
    const end = calendarEndArray.value[index]
    return getCalendarError(start) !== '' || getCalendarError(end) !== ''
  })
  if (hasError) {
    return
  }

  emit('filter', true)
}

/**
 * カレンダーのエラーメッセージを取得 (YYYY/MM/DD形式で、存在しない日付もエラー)
 * @param value 入力された値
 * @returns エラーメッセージ
 */
const getCalendarError = (value: string) => {
  if (value === '') return ''

  // 形式チェック
  const datePattern = /^\d{4}\/\d{1,2}\/\d{1,2}$/
  if (!datePattern.test(value)) {
    return 'YYYY/MM/DD形式で入力してください'
  }

  const [yearStr, monthStr, dayStr] = value.split('/')
  const year = Number(yearStr)
  const month = Number(monthStr)
  const day = Number(dayStr)

  if (month < 1 || month > 12) {
    return '月は1〜12の間で入力してください'
  }

  if (day < 1 || day > 31) {
    return '日は1〜31の間で入力してください'
  }

  // 日付の存在チェック
  const date = new Date(year, month - 1, day)
  if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
    return '存在しない日付です'
  }

  return ''
}
</script>

<template>
  <div>
    <div class="_serch_container" @click="searchMenuOpen">
      <span class="_serch_text">search</span>
      <CCMDIcon name="manage_search" class="_search_icon" />
    </div>
    <transition name="slide">
      <div class="_back_ground" v-show="isSearchMenu">
        <CCCard id="menuArea" class="_menu_container">
          <CCMDIcon @click="searchMenuClose" name="arrow_forward" class="_arrow_icon" />
          <div class="_feature_container">
            <div
              v-if="isInputType"
              v-for="(input, index) in inputObjArray"
              :key="index"
              class="_form_container"
            >
              <div @click="valueClear(input.filterType, index)" class="_clear_button">
                ×&thinsp;クリア
              </div>
              <CCInput
                v-model="inputValueArray[index]"
                :label="input.label"
                :placeholder="input.placeholder"
                shadow
              />
            </div>
            <div
              v-if="isSelectType"
              v-for="(select, index) in selectObjArray"
              :key="index"
              class="_form_container"
            >
              <div @click="valueClear(select.filterType, index)" class="_clear_button">
                ×&thinsp;クリア
              </div>
              <CCSelect
                v-model="selectValueArray[index]"
                :options="selectOptionsArray[index]"
                :label="select.label"
                shadow
                :placeholder="select.placeholder"
              />
            </div>
            <div
              v-if="isCheckboxesType"
              v-for="(checkbox, checkboxIndex) in checkboxesObjArray"
              :key="checkboxIndex"
              class="_checkbox_radio_container"
            >
              <div class="_checbox_label">{{ checkbox.label }}</div>
              <div @click="valueClear(checkbox.filterType, checkboxIndex)" class="_clear_button">
                ×&thinsp;クリア
              </div>
              <div class="_check_radio_content">
                <div v-for="(label, labelIndex) in checkbox.options" :key="label">
                  <CCCheckbox
                    :label="label"
                    v-model="checkboxesValueArray[checkboxIndex][labelIndex]"
                    labelColor="black"
                  />
                </div>
              </div>
            </div>
            <div
              v-if="isRadioType"
              v-for="(radio, radioIndex) in radioObjArray"
              :key="radioIndex"
              class="_checkbox_radio_container"
            >
              <div class="_checbox_label">{{ radio.label }}</div>
              <div @click="valueClear(radio.filterType, radioIndex)" class="_clear_button">
                ×&thinsp;クリア
              </div>
              <div class="_check_radio_content">
                <div v-for="label in radio.options" :key="label">
                  <CCRadio :value="label" v-model="radioValueArray[radioIndex]" />
                </div>
              </div>
            </div>
            <div
              v-if="isCalendarType"
              v-for="(calendar, index) in calendarObjArray"
              :key="index"
              class="_form_container"
            >
              <div class="_calendar_form">
                <div @click="valueClear(calendar.filterType, index)" class="_clear_button">
                  ×&thinsp;クリア
                </div>

                <CCInput
                  v-model="calendarStartArray[index]"
                  :label="calendar.label"
                  shadow
                  date
                  placeholder="開始する日を選択"
                  :error4="getCalendarError(calendarStartArray[index])"
                />
                <CCInput
                  v-model="calendarEndArray[index]"
                  shadow
                  date
                  placeholder="終了する日を選択"
                  :error4="getCalendarError(calendarEndArray[index])"
                />
              </div>
            </div>
            <div class="_button_container">
              <CCButton
                @click="allClear()"
                style="background: #333333"
                label="全ての条件をクリア"
              />
              <CCButton @click="runSearch()" style="background: #00bbc9" label="検索" />
            </div>
          </div>
        </CCCard>
      </div>
    </transition>
  </div>
</template>

<style lang="sass" scoped>
._serch_container
  display: flex
  align-items: center
  gap: 15px
  cursor: pointer
  transition: 0.3s
._serch_container:hover
  opacity: 0.7
._serch_text
  font-size: 20px
._search_icon
  font-size: 40px
._menu_container
  background: #D2D2D2
  width: 325px
  min-height: 100%
  position: fixed
  top: 0
  bottom: 0
  right: 0
  overflow-y: scroll
  z-index: 1
._feature_container
  width: 80%
  margin: 20px auto 0 auto
  display: flex
  flex-direction: column
  row-gap: 20px
._form_container
  display: flex
  flex-direction: column
  position: relative
._checkbox_radio_container
  position: relative
  display: flex
  flex-direction: column
._checbox_label
  padding: 0 5px
  font-size: 16px
  color: #707070
  width: fit-content
._check_radio_content
  background: #FFFFFF
  padding: 20px 20px 20px 15px
  border-radius: 5px
._calendar_form
  display: flex
  flex-direction: column
  row-gap: 10px
._clear_button
  font-size: 12px
  font-weight: bold
  color: var(--pm-text-black)
  position: absolute
  right: 0
  top: 2px
  z-index: 1
  cursor: pointer
._back_ground
  position: fixed
  right: 0px
  top: 0px
  width: 100%
  height: 100vh
  overflow-y: scroll
  z-index: 100
._button_container
  display: flex
  flex-direction: column
  row-gap: 20px
  margin-top: 60px
  margin-bottom: 80px
._arrow_icon
  font-size: 50px
  font-weight: bold
  color: #333333
  margin: 10px 0 0 10px
  cursor: pointer
  transition: 0.3s
::v-deep(._select_form_container)
  background-color: white
._arrow_icon:hover
  opacity: 0.7
.slide-enter-active, .slide-leave-active
  transition: transform 0.5s ease
.slide-enter-from, .slide-leave-to
  transform: translateX(100%)
.slide-enter-to, .slide-leave-from
  transform: translateX(0)
</style>
