<script setup>
import { ref, computed, watch, onMounted } from 'vue'
import CCalendar from '../CCalendar/CCalendar.vue'
import MDIcon from '../MDIcon/MDIcon.vue'

const props = defineProps({
  /**
   * id
   * @example id: 'inputId'
   */
  id: { type: String, default: '' },
  /**
   * @see https://developer.mozilla.org/ja/docs/Web/HTML/Element/input#input_types
   */
  type: { type: String, default: 'text' },
  /**
   * 値
   * @example modelValue: ''
   */
  modelValue: { type: [String, Number, Boolean, Date], default: '' | 0 | null },
  /**
   * タイトル
   * @example p_Title: 'メールアドレス'
   */
  p_Title: { type: String, default: '' },
  /**
   * マックスレングス
   * @example p_Maxlength: 10
   */
  p_Maxlength: { type: Number, default: 999 },
  /**
   * プレスホルダー
   * @example p_Placeholder: '名前を入力してください'
   */
  p_Placeholder: { type: String, default: '' },
  /**
   * 制御
   * @example disabled: true
   */
  disabled: { type: Boolean, default: false },
  /**
   * エラー
   * @example p_Error: true
   */
  p_Error: { type: Boolean, default: false },
  /**
   * Adds a debounce. Awaits the number given (in ms) before the input text is emitted.
   * @example p_Debounce: 500
   */
  p_Debounce: { type: Number, default: 0 },
  /**
   * カレンダーid
   * @example p_CalendarId: 'calendarId1'
   */
  p_CalendarId: { type: String, default: '' },
  /**
   * カレンダーボタンid
   * @example p_CalendarBtnId: 'calendarBtnId2'
   */
  p_CalendarBtnId: { type: String, default: '' },
  /**
   * 注意文：nullの場合は非表示
   * @example p_Caution: '※注意分'
   */
  p_Caution: { type: String, default: '' },
  /**
   * スモールモード
   * @example p_SmallMode: true
   */
  p_SmallMode: { type: Boolean, default: false },
  /**
   * numberの最大値
   * @example max: 1000
   */
  max: { type: Number, default: undefined },
  /**
   * numberの最小値
   * @example min: 0
   */
  min: { type: Number, default: undefined },
  /**
   * numberの数字刻み
   * @example step: 1
   */
  step: { type: Number, default: 1 },
  /**
   * text時の入力制限
   * @example pattern: 1
   */
  pattern: { type: Number, default: undefined },
  /**
  /**
   inputmode
   * @example inputmode: numeric
   */
  inputmode: { type: String, default: 'text' },
  /**
   * 表示位置の調整可否のフラグ
   * @example p_AutoPosition: true
   */
  p_AutoPosition: { type: Boolean, default: true },
})

const emit = defineEmits(['update:modelValue', 'onBlur', 'onFocus'])

let debounceTimeout = null

const model = computed({
  get() {
    return props.modelValue
  },
  set(val) {
    clearTimeout(debounceTimeout)
    debounceTimeout = setTimeout(() => emit('update:modelValue', val), props.p_Debounce)
  },
})

// typeの初期値を設定
const typeLocal = ref(props.type)

// カレンダーの表示状態を定義する
const isDispCalendar = ref(false)
// カレンダーのidを定義する
const isCalendarId = ref(props.p_CalendarId)
// カレンダーボタンidを定義する
const isCalendarBtnId = ref(props.p_CalendarBtnId)
// カレンダーとカレンダーボタンとその他スペースのクリック位置を定義する
const calcCalendarBtn = ref(0)
const calcCalendar = ref(0)
const calcSpace = ref(0)

onMounted(() => {
  // カレンダーidとカレンダーボタンidが設定されている場合、カレンダーとボタンとその他スペースのクリック位置を取得する
  if (isCalendarId.value && isCalendarBtnId.value) {
    // カレンダーボタンのクリック位置を取得する
    document.getElementById(isCalendarBtnId.value).addEventListener('click', (event) => {
      calcCalendarBtn.value = event.pageX + event.pageY
    })
    // カレンダーのクリック位置を取得する
    document.getElementById(isCalendarId.value).addEventListener('click', (event) => {
      calcCalendar.value = event.pageX + event.pageY
    })
    // その他スペースのクリック位置を取得する
    document.body.addEventListener('click', (event) => {
      calcSpace.value = event.pageX + event.pageY
    })
  }
})

// パスワードアイコンをクリックする
const clickPasswordIcon = () => {
  if (typeLocal.value === 'password') {
    typeLocal.value = 'text'
  } else {
    typeLocal.value = 'password'
  }
}

// ボタンをクリックしたか判断するフラグを定義する
const calendarBtnFlg = ref(false)

// カレンダーアイコンをクリックする
const clickCalendarIcon = (event) => {
  // アイコンがクリックされた場合、またはdisableがfalseの場合のみ展開させる
  if (event.detail !== 0 && !props.disabled) {
    // カレンダーを上下どちらに表示するか処理を行う
    const targetElement = document.getElementById(isCalendarBtnId.value)
    const clientRect = targetElement.getBoundingClientRect()
    // 上下の表示位置を決定する
    if (props.p_AutoPosition && clientRect.top > window.innerHeight - clientRect.bottom) {
      // 上に表示する
      document.getElementById(isCalendarId.value).style.top = '-285px'
    } else if (props.p_AutoPosition) {
      // 下に表示する
      document.getElementById(isCalendarId.value).style.top = '75px'
    }
    // // 左右の表示位置を決定する
    // // 横幅の中央位置を定義する
    // const widthCenter = window.innerWidth / 2
    // // 中央位置より右側にいた場合
    // if (widthCenter < clientRect.left) {
    //   // 左よりに表示する
    //   document.getElementById(isCalendarId.value).style.right = '0px'
    // }
    isDispCalendar.value = !isDispCalendar.value
    if (isDispCalendar.value) {
      calendarBtnFlg.value = true
    } else {
      calendarBtnFlg.value = false
    }
  }
}

// カレンダーの値を定義する
const isDate = ref(undefined)
const resultDate = ref('')
// クリックした箇所によってカレンダー表示の処理を分岐する
watch(calcSpace, () => {
  if (!props.disabled) {
    // ボタンをクリックした場合、カレンダーを展開させる
    if (calcSpace.value === calcCalendarBtn.value && calendarBtnFlg.value) {
      isDispCalendar.value = true
    } else if (calcSpace.value === calcCalendar.value) {
      // カレンダーをクリックした場合
      if (resultDate.value) {
        // 値が選択された場合、カレンダーを閉じる
        isDispCalendar.value = false
        resultDate.value = ''
      } else if (!resultDate.value && calendarBtnFlg.value) {
        // 値が選択されていない場合、展開を維持する
        isDispCalendar.value = true
      }
    } else if (calcSpace.value !== calcCalendarBtn.value) {
      // カレンダー以外のスペースをクリックした場合、カレンダーは閉じる
      isDispCalendar.value = false
    }
  }
})
watch(isDate, () => {
  const year = isDate.value.year
  const month = ('0' + isDate.value.month).slice(-2)
  const day = ('0' + isDate.value.day).slice(-2)
  const result = year + '/' + month + '/' + day
  resultDate.value = result
  emit('update:modelValue', result)
})

// typeカレンダーでmodelValueが空欄の状態でエンターを押した場合、カレンダーを展開する
const enterSubmit = (event) => {
  if (event.code === 'Enter' && props.type === 'calendar' && props.modelValue === '') {
    // カレンダーを上下どちらに表示するか処理を行う
    const targetElement = document.getElementById(isCalendarBtnId.value)
    const clientRect = targetElement.getBoundingClientRect()
    // 上下の表示位置を決定する
    if (props.p_AutoPosition && clientRect.top > window.innerHeight - clientRect.bottom) {
      // 上に表示する
      document.getElementById(isCalendarId.value).style.top = '-285px'
    } else if (props.p_AutoPosition) {
      // 下に表示する
      document.getElementById(isCalendarId.value).style.top = '75px'
    }
    // 左右の表示位置を決定する
    // 横幅の中央位置を定義する
    const widthCenter = window.innerWidth / 2
    // 中央位置より右側にいた場合
    if (widthCenter < clientRect.left) {
      // 左よりに表示する
      document.getElementById(isCalendarId.value).style.right = '0px'
    }

    isDispCalendar.value = true
  }
}

// カレンダーの展開状態を監視する
watch(isDispCalendar, () => {
  // もしtypeがcalendarでカレンダーが展開している場合
  if (props.type === 'calendar' && isDispCalendar.value) {
    // タブ移動をさせない
    window.onkeydown = function (e) {
      if (e.keyCode == 9) return false
    }
  } else {
    // タブ移動をさせる
    window.onkeydown = function (e) {
      if (e.keyCode == 9) return true
    }
  }
})

// カレンダーで選択された値を監視する
watch(
  () => props.modelValue,
  () => {
    // typeがカレンダーで値がセットされた場合、インプットフォームにフォーカスを当てる
    if (props.type === 'calendar' && props.modelValue) {
      document.getElementById(props.id)?.focus()
    }
  }
)
const onBlur = () => {
  emit('onBlur')
}
const onFocus = () => {
  emit('onFocus')
}
</script>

<template>
  <!-- インプットボックス -->
  <div class="_input_box">
    <!-- カレンダー -->
    <div v-show="type === 'calendar' && isDispCalendar">
      <CCalendar :id="isCalendarId" v-model="isDate" p_Color="_primary" class="_calendar" />
    </div>
    <!-- タイトル -->
    <div v-if="p_Title" class="_title">
      {{ p_Title }}
      <div v-if="p_Caution" class="_caution">{{ p_Caution }}</div>
    </div>
    <!-- インプット -->
    <div :class="{ _inner_box: type !== 'search', _inner_box_search: type === 'search' }">
      <input
        :id="id"
        v-model="model"
        :type="typeLocal"
        :maxlength="type === 'calendar' ? 10 : p_Maxlength"
        :placeholder="
          type === 'search'
            ? 'キーワードで検索'
            : p_Placeholder
            ? p_Placeholder
            : type === 'calendar'
            ? 'YYYY/MM/DD'
            : ''
        "
        :max="max"
        :min="min"
        :step="step"
        :pattern="pattern"
        :inputmode="inputmode"
        :disabled="disabled"
        class="_input"
        :class="{
          _disable_style: disabled,
          _error_style: p_Error,
          _left_icon_style: type === 'search',
          _right_icon_style: type === 'password' || type === 'calendar',
          _smallmode_on: p_SmallMode,
          _smallmode_off: !p_SmallMode,
        }"
        @keyup.enter="enterSubmit"
        @blur="onBlur()"
        @focus="onFocus()"
      />

      <!-- サーチアイコン -->
      <MDIcon
        v-if="type === 'search'"
        name="search"
        class="_c_material _left_icon"
        type="outlined"
        :p_PointerCursor="false"
      />

      <!-- パスワードアイコン -->
      <div v-if="type === 'password'">
        <!-- false -->
        <MDIcon
          v-if="typeLocal === 'password'"
          name="visibility_off"
          class="_c_material _right_icon"
          type="outlined"
          @click="clickPasswordIcon"
        />
        <!-- true -->
        <MDIcon
          v-if="typeLocal !== 'password'"
          name="visibility"
          class="_c_material _right_icon"
          type="outlined"
          @click="clickPasswordIcon"
        />
      </div>

      <!-- カレンダーアイコン -->
      <div v-if="type === 'calendar'">
        <MDIcon
          :id="isCalendarBtnId"
          name="calendar_today"
          class="_c_material"
          :class="{ _right_icon: !disabled, _disable_icon: disabled }"
          type="outlined"
          @click="clickCalendarIcon"
        />
      </div>
    </div>
  </div>
</template>

<style lang="sass" scoped>
// インプットボックス
._input_box
  position: relative

// カレンダー
._calendar
  position: absolute
  z-index: 10

// インプットボックス
._inner_box
  position: relative
// 検索フォームインプットボックス
._inner_box_search
  position: relative
  width: 200px

// タイトル
._title
  font-size: 12px
  color: #707070
  margin-bottom: 5px

// 注意文
._caution
  display: inline-block
  margin-left: 5px
  color: #FF0101

// インプット
._input
  width: 100%
  outline: none
  border: none
  box-shadow: 0 0 5px 0 rgb(0 0 0 / 16%)
  border-radius: 4px
  padding: 0px 10px
// スモールモードオン
._smallmode_on
  height: 30px
// スモールモードオフ
._smallmode_off
  height: 40px

// disablesカラー
._disable_style
  background: #EFEFEF
  cursor: not-allowed

// errorカラー
._error_style
  background: var(--q-warning, #FF6D6D)

// 左アイコンスタイル
._left_icon_style
  padding-left: 40px

// 右アイコンスタイル
._right_icon_style
  padding-right: 50px

// マテリアルアイコン
._c_material
  position: absolute
  font-size: 30px
  color: #707070
  top: 6px
// 左アイコン
._left_icon
  left: 6px
// 右アイコン
._right_icon
  right: 10px
  cursor: pointer
  transition: 0.3s
._right_icon:hover
  opacity: 0.7
  transition: 0.3s
._disable_icon
  right: 10px
  cursor: not-allowed
</style>
