<script setup lang="ts">
import { PropType, computed, onMounted, ref, onBeforeUnmount } from 'vue'
import { CCCard } from '../../index'
import { ColorTypes } from '../../types'

const props = defineProps({
  /**
   * カラー
   * @example 'primary' | 'secondary' | 'positive' | 'negative' | 'warning'
   */
  color: {
    type: String as PropType<ColorTypes>,
    default: 'secondary',
  },
  /**
   * ボタンのサイズ(afc用)
   */
  miniSize: { type: Boolean, default: false },
  /**
   * アップロード済みの写真かどうか
   * @example uploaded
   */
  uploaded: { type: Boolean, default: true },
  /**
   * ボタンの透明度
   * @example opacity
   */
  opacity: { type: Boolean, default: false },
  /**
   *
   */
  modelValue: { type: Boolean },
})

const emit = defineEmits<{
  (e: 'update:modelValue', value: boolean): void
  (e: 'onClick', action: 'download' | 'delete' | 'shareUrl' | 'edit'): void
  (e: 'fileChange', file: File): void
}>()

onMounted(() => {
  const menuButton = document.getElementById('menuButton')
  if (menuButton) {
    const rect = menuButton.getBoundingClientRect()
    teleportPosition.value = {
      top: rect.top + window.scrollY,
      left: rect.left + window.scrollX,
    }
  }
})

/** メニューの表示非表示フラグ */
const isShowCard = computed<boolean>({
  get() {
    return props.modelValue
  },
  set(value) {
    emit('update:modelValue', value)
  },
})

/** メニューの表示非表示を切り替える */
const toggleCard = () => {
  isShowCard.value = !isShowCard.value
  updateMenuPosition() // メニューが表示される度に位置を更新
}

const menuButtonRef = ref<HTMLElement | null>(null)
const menuCardRef = ref<HTMLElement | null>(null)
const handleClickOutside = (e: MouseEvent) => {
  if (!menuButtonRef.value || !menuCardRef.value) return

  if (
    !menuButtonRef.value.contains(e.target as Node) &&
    !menuCardRef.value.contains(e.target as Node) &&
    isShowCard.value
  ) {
    isShowCard.value = false
  }
}

onMounted(() => {
  document.addEventListener('click', handleClickOutside)
})

onBeforeUnmount(() => {
  document.removeEventListener('click', handleClickOutside)
})

/** 渡ってきたカラーに応じて色を変える */
const changeColor = computed(() => {
  if (props.color === 'primary') return 'var(--c-primary)'
  if (props.color === 'secondary') return 'var(--c-secondary)'
  if (props.color === 'positive') return 'var(--c-positive)'
  if (props.color === 'negative') return 'var(--c-negative)'
  if (props.color === 'warning') return 'var(--c-warning)'
})
const teleportPosition = ref({ top: 0, left: 0 })

/**
 * メニューカードの位置を更新する
 */
/**
 * メニューカードの位置を更新する
 */
const updateMenuPosition = () => {
  const menuButton = menuButtonRef.value
  if (menuButton) {
    const rect = menuButton.getBoundingClientRect()

    const cardWidth = 130 // メニューカードの幅
    const cardHeight = 200 // メニューカードの高さ
    const windowWidth = window.innerWidth
    const windowHeight = window.innerHeight

    // メニューカードが画面右端にはみ出す場合または画面左端にはみ出す場合
    if (rect.left + cardWidth > windowWidth || rect.right + cardWidth > windowWidth) {
      teleportPosition.value = {
        top: rect.top - 120, // ボタンの上に表示し、さらに100px上にずらす
        left: rect.left - cardWidth, // ボタンの左側から170px右にずらす
      }
    }
    // メニューカードが画面下端にはみ出す場合
    else if (rect.bottom + cardHeight > windowHeight) {
      teleportPosition.value = {
        top: rect.top - cardHeight + 90, // ボタンの上に表示し、さらに50px上にずらす
        left: rect.left - 150,
      }
    }
    // メニューカードが画面内に収まる場合
    else {
      teleportPosition.value = {
        top: rect.top + 50, // ボタンの下に表示し、さらに50px下にずらす
        left: rect.left,
      }
    }
  }
}

// 画面サイズが変更された時にメニューの位置を更新する
window.addEventListener('resize', updateMenuPosition)

onMounted(() => {
  updateMenuPosition()
})

onBeforeUnmount(() => {
  window.removeEventListener('resize', updateMenuPosition)
})
</script>

<template>
  <div
    ref="menuButtonRef"
    :class="['_circle', { _opacity: opacity, _circle_mini: miniSize }]"
    @click="toggleCard()"
  >
    <div :class="['_line', { _line_mini: miniSize }]"></div>
    <div :class="['_line', { _line_mini: miniSize }]"></div>
    <div :class="['_line', { _line_mini: miniSize }]"></div>
  </div>
  <Teleport to="body" ref="menuCardRef">
    <transition name="fade">
      <CCCard
        class="_card"
        v-show="isShowCard"
        :style="{ top: teleportPosition.top + 'px', left: teleportPosition.left + 'px' }"
      >
        <div v-if="uploaded">
          <div class="_button" @click="() => emit('onClick', 'download')">ダウンロード</div>
          <div class="_button" @click="() => emit('onClick', 'edit')">編集</div>
          <div class="_button" @click="() => emit('onClick', 'delete')">削除</div>
          <div class="_button" @click="() => emit('onClick', 'shareUrl')">共有用URL</div>
        </div>
        <div v-else>
          <div class="_button" @click="() => emit('onClick', 'edit')">編集</div>
          <div class="_button" @click="() => emit('onClick', 'delete')">削除</div>
        </div>
        <div class="_button" @click="toggleCard()">閉じる</div>
      </CCCard>
    </transition>
  </Teleport>
</template>

<style lang="sass" scoped>
._circle
  width: 40px
  height: 40px
  background: rgba(255, 255, 255)
  border-radius: 50%
  position: relative
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1)
  cursor: pointer
._circle_mini
  width: 20px
  height: 20px

._opacity
  background: rgba(255, 255, 255, 0.5)
._line
  position: absolute
  left: 50%
  top: 50%
  transform: translate(-50%, -50%)
  width: 20px
  height: 2px
  background: v-bind(changeColor)
._line_mini
  width: 10px
  height: 10px
  height: 0.1px
._line:nth-child(2)
  top: 65%
._line:nth-child(3)
  top: 35%
._card
  // サイズの変更をする際は座標も変更してください
  width: 130px
  margin-top: 10px
  margin: 0 auto
  z-index: 9999999
  position: absolute

.fade-enter-active, .fade-leave-active
  transition: opacity 0.2s
.fade-enter, .fade-enter-from, .fade-leave-to
  opacity: 0
.fade-enter-to, .fade-leave
  opacity: 1
._button
  font-size: 16px
  color: #707070
  padding: 10px 15px
  cursor: pointer
  transition: 0.3s
._button:hover
  background: v-bind(changeColor)
  color: #ffffff
</style>
