<script setup lang="ts">
import { ref, watch, PropType } from 'vue'
import { CCButton, CCMDIcon, CCImageMenuButton } from '../../index'
import { SizeTypes, ColorTypes } from '../../types'
import pdfIcon from '../../assets/pdf_icon.svg'
// import pdfIcon from '/image/pdf-icon.svg'

const props = defineProps({
  modelValue: { type: Object as PropType<File | null | undefined> },
  /**
   * ボタンのラベル
   * @example 'ファイルを選択'
   */
  label: { type: String, default: 'ファイルを選択' },
  /**
   * outline
   * @example outline
   */
  outline: { type: Boolean, default: false },
  /**
   * ラウンドデザイン
   * @example rounded
   */
  rounded: { type: Boolean, default: false },
  /**
   * スクエアデザイン
   * @example square
   */
  square: { type: Boolean, default: false },
  /**
   * ボタンのサイズ
   * @example 'xs' | 'XS' | 's' | 'S' | 'm' | 'M' | 'l' | 'L' | 'xl' | 'XL'
   */
  size: { type: String as PropType<SizeTypes>, default: 'xs' },
  /**
   * 画像幅
   * -allMiniSizeの場合は使用しない
   */
  imageWidth: { type: String, default: '260px' },
  /**
   * 画像高さ
   * -allMiniSizeの場合は使用しない
   */
  imageHeight: { type: String, default: '175px' },
  /**
   * allMiniSize
   * 全てのサイズを最小にする（AFC用）
   */
  allMiniSize: { type: Boolean, default: false },
  /**
   * カラー
   * @example 'primary' | 'secondary' | 'positive' | 'negative' | 'warning'
   */
  color: {
    type: String as PropType<ColorTypes>,
    default: 'secondary',
  },
  /**
   * 画像のURL(表示のみを行う)
   */
  url: { type: String, default: '' },

  /**
   * アップロード済みの写真かどうか
   * @example uploaded
   */
  uploaded: { type: Boolean, default: true },
  /**
   * メニューの項目を選択して表示
   * @example showCustomMenu
   */
  showCustomMenu: {
    type: Object as PropType<{
      download: boolean
      delete: boolean
      shareUrl: boolean
      edit: boolean
      close: boolean
    }>,
    default: {
      download: true,
      delete: true,
      shareUrl: true,
      edit: true,
      close: true,
    },
  },
  /**
   * メニューの表示非表示
   */
  isNotMenu: { type: Boolean, default: false },
  /**
   * pdfタイトル
   */
  pdfTitle: { type: String, default: '' },
  /**
   * mimeタイプ
   * 2パターンのみ
   * 影響しないように空も許容
   */
  mime: { type: String, default: '' },
  /**
   * mimeタイプ
   * 2パターンのみ
   * 影響しないように空も許容
   */
  accept: { type: String, default: '' },
  /**
   * nullの際にアップロード禁止プロパティ
   * - allMiniSizeのみ対応
   */
  isNotUpload: { type: Boolean, default: false },
  /**
   * ドラックアンドドロップを単一のみ許可をするかどうか
   */
  isSingleDrop: { type: Boolean, default: false },
  /**
   * disabled
   */
  disabled: { type: Boolean, default: false },
})
/** 画像のプレビュー用URL */
const imagePreviewUrl = ref<string | null>(null)
/** 画像のFileオブジェクト */
const imageFileObj = ref<File | null>(null)
/** PDFのプレビュー用URL */
const pdfPreviewUrl = ref<string | null>(null)
/** PDFのFileオブジェクト */
const pdfFileObj = ref<File | null>(null)
/** 動画のプレビュー用URL */
const videoPreviewUrl = ref<string | null>(null)
/** 動画のFileオブジェクト */
const videoFileObj = ref<File | null>(null)
/** inputエレメントを参照するためのref */
const fileInput = ref<HTMLInputElement | null>(null)
/** ドラッグしているファイルが範囲内にあるかのフラグ */
const isDragOver = ref(false)
/** 画像拡大時のモーダルフラグ */
const isModalImage = ref(false)
/** PDF拡大時のモーダルフラグ */
const isModalPdf = ref(false)
/** video拡大時のモーダルフラグ */
const isModalVideo = ref(false)
/** メニューの表示切り替え */
const isMenu = ref(false)

const emit = defineEmits<{
  (e: 'update:modelValue', value: File | null): void
  // 編集ボタン
  (e: 'onSelect'): void
  (e: 'storageToFile', file: File): void
  // 削除ボタン
  (e: 'fileObjDelete'): void
  // 共有
  (e: 'shareUrl'): void
  // ダウンロード
  (e: 'download'): void
}>()

/** ファイル選択のウィンドウを開く */
const chooseFile = () => {
  if (props.isNotUpload) return
  if (fileInput.value) {
    fileInput.value.click()
  }
}

watch(
  () => [props.url, props.mime],
  () => {
    if (props.url && props.mime.includes('image')) {
      imagePreviewUrl.value = props.url
      return
    }
    if (props.url && props.mime.includes('pdf')) {
      pdfPreviewUrl.value = props.url
      return
    }
    if (props.url && props.mime.includes('video')) {
      videoPreviewUrl.value = props.url
      return
    }
    if (props.url) {
      imagePreviewUrl.value = props.url
    }
  },
  { immediate: true }
)
const isCheckAccept = (fileType: string, accept: string): boolean => {
  // 拡張子を取得する
  const fileExtension = fileType.split('/').pop()?.toLowerCase()

  // 受け入れ可能な拡張子のリストを取得し、各拡張子をトリムして配列に格納します
  const acceptedExtensions = accept.split(',').map((ext) => ext.trim().toLowerCase())

  // 受け入れ可能な拡張子のリストをループします
  for (const acceptedExtension of acceptedExtensions) {
    // ワイルドカードがある場合、全てのファイルを許可します
    if (acceptedExtension === '*') {
      return true
    }
    // 受け入れ可能なタイプが image/* で、かつファイルの MIME タイプが image/* で始まる場合、画像ファイルを許可します
    if (acceptedExtension.startsWith('image/') && fileType.startsWith('image/')) {
      return true
    }
    // 拡張子に対応する場合、そのファイルを許可します
    if (
      acceptedExtensions.includes(`${fileExtension}`) ||
      acceptedExtensions.includes(`.${fileExtension}`)
    ) {
      return true
    }
    // 拡張子に対応する MIME タイプがある場合、そのファイルを許可します（例: svg）
    if (acceptedExtension === 'svg' && fileType === 'image/svg+xml') {
      return true
    }
  }
  // 上記の条件に一致しない場合、ファイルは許可されません
  return false
}

/** 選択されたFileオブジェクトを親コンポーネントに渡す */
const handleFilePreview = (file: File) => {
  if (props.accept) {
    if (isCheckAccept(file.type, props.accept) && file.type.startsWith('image/')) {
      // 画像ファイルの処理
      imagePreviewUrl.value = URL.createObjectURL(file)
      imageFileObj.value = file
      pdfPreviewUrl.value = null
      pdfFileObj.value = null
      videoPreviewUrl.value = null
      videoFileObj.value = null
      emit('storageToFile', imageFileObj.value)
      emit('update:modelValue', imageFileObj.value)
      return
    }
    if (file.type === 'application/pdf' && props.accept.includes('pdf')) {
      // PDFファイルの処理
      pdfPreviewUrl.value = URL.createObjectURL(file)
      pdfFileObj.value = file
      imagePreviewUrl.value = null
      imageFileObj.value = null
      videoPreviewUrl.value = null
      videoFileObj.value = null
      emit('storageToFile', pdfFileObj.value)
      emit('update:modelValue', pdfFileObj.value)
      return
    }
    if (file.type.startsWith('video/') && props.accept.includes('video')) {
      // 動画ファイルの処理
      videoPreviewUrl.value = URL.createObjectURL(file)
      videoFileObj.value = file
      imagePreviewUrl.value = null
      imageFileObj.value = null
      pdfPreviewUrl.value = null
      pdfFileObj.value = null
      emit('storageToFile', videoFileObj.value)
      emit('update:modelValue', videoFileObj.value)
      return
    }
    // 許可されていない拡張子の処理
    alert(
      '設定された拡張子以外のファイルが選択されました。許可されている拡張子のファイルを選択してください。'
    )
    return
  }

  // `accept` が指定されていない場合、すべてのファイルを許可
  if (file.type.startsWith('image/')) {
    imagePreviewUrl.value = URL.createObjectURL(file)
    imageFileObj.value = file
    pdfPreviewUrl.value = null
    pdfFileObj.value = null
    videoPreviewUrl.value = null
    videoFileObj.value = null
    emit('storageToFile', imageFileObj.value)
    emit('update:modelValue', imageFileObj.value)
  } else if (file.type === 'application/pdf') {
    pdfPreviewUrl.value = URL.createObjectURL(file)
    pdfFileObj.value = file
    imagePreviewUrl.value = null
    imageFileObj.value = null
    videoPreviewUrl.value = null
    videoFileObj.value = null
    emit('storageToFile', pdfFileObj.value)
    emit('update:modelValue', pdfFileObj.value)
  } else if (file.type.startsWith('video/')) {
    videoPreviewUrl.value = URL.createObjectURL(file)
    videoFileObj.value = file
    imagePreviewUrl.value = null
    imageFileObj.value = null
    pdfPreviewUrl.value = null
    pdfFileObj.value = null
    emit('storageToFile', videoFileObj.value)
    emit('update:modelValue', videoFileObj.value)
  } else {
    // 許可されていない拡張子の処理
    alert('画像・PDF・動画ファイルを選択してください。')
  }
}

/** 選択、またはドロップされたデータをFileオブジェクトに変換 */
const onFileChangeAndDrop = (event: Event) => {
  // 複数のファイルが選択された場合、isSingleDropがtrueの場合、エラーを返す
  if (props.isSingleDrop) {
    if (event.target instanceof HTMLInputElement) {
      if (event.target.files?.length !== 1) {
        alert('複数のファイルが選択されました。単一のファイルを選択してください。')
        return
      }
    }
    if (event instanceof DragEvent) {
      if (event.dataTransfer?.files.length !== 1) {
        alert('複数のファイルが選択されました。単一のファイルを選択してください。')
        return
      }
    }
  }
  if (props.isNotUpload) return
  if (event.target instanceof HTMLInputElement) {
    const file = event.target.files?.[0]
    handleFilePreview(file as File)
  }
  if (event instanceof DragEvent) {
    const file = event.dataTransfer?.files[0]
    handleFilePreview(file as File)
  }
}

/** メニューボタンのアクション機能 */
const menuAction = (action: 'download' | 'delete' | 'shareUrl' | 'edit') => {
  if (action === 'delete') {
    imagePreviewUrl.value = null
    imageFileObj.value = null
    pdfPreviewUrl.value = null
    pdfFileObj.value = null
    videoPreviewUrl.value = null
    videoFileObj.value = null
    isDragOver.value = false
    emit('fileObjDelete')
  }

  if (action === 'edit') chooseFile()

  if (action === 'shareUrl') emit('shareUrl')

  if (action === 'download') emit('download')

  isMenu.value = false
}

/** 子コンポーネントからemitで受け取ったFileオブジェクトを関数に渡す */
const fileChange = (file: File) => {
  handleFilePreview(file)
}

/** ドラッグしているファイルが範囲内にある場合の処理 */
const onDragover = () => {
  if (props.isNotUpload) return
  isDragOver.value = true
}

/** ドラッグしているファイルが範囲外にある場合の処理 */
const onDragleave = () => {
  if (props.isNotUpload) return
  isDragOver.value = false
}

/** 画像・PDF・動画拡大時のモーダルを開く */
const openModal = (modal: string) => {
  if (modal === 'image') {
    isModalImage.value = true
  } else if (modal === 'pdf') {
    isModalPdf.value = true
  } else if (modal === 'video') {
    isModalVideo.value = true
  }
  document.body.style.overflow = 'hidden'
}

/** 画像・PDF・動画拡大時のモーダルを閉じる */
const closeModal = (modal: string) => {
  if (modal === 'image') {
    isModalImage.value = false
  } else if (modal === 'pdf') {
    isModalPdf.value = false
  } else if (modal === 'video') {
    isModalVideo.value = false
  }
  document.body.style.overflow = 'auto'
}
</script>

<template>
  <div v-if="imagePreviewUrl" style="user-select: none">
    <input
      type="file"
      ref="fileInput"
      style="display: none"
      @change="onFileChangeAndDrop"
      :accept="accept"
    />
    <div :class="{ _preview_container_mini: allMiniSize, _preview_container: !allMiniSize }">
      <img :src="imagePreviewUrl" class="_preview_image" @click="openModal('image')" />
      <div class="_menu_button" v-if="!isNotMenu">
        <CCImageMenuButton
          :uploaded="true"
          :color="color"
          :miniSize="allMiniSize"
          :showCustomMenu="showCustomMenu"
          opacity
          v-model="isMenu"
          @onClick="menuAction"
          @fileChange="fileChange"
        />
      </div>
    </div>
    <Teleport to="body">
      <div v-if="isModalImage" class="_modal">
        <div class="_modal_background" @click="closeModal('image')"></div>
        <img :src="imagePreviewUrl" class="_modal_image" />
        <CCMDIcon name="close" @click="closeModal('image')" class="_modal_close" />
      </div>
    </Teleport>
  </div>

  <div v-else-if="pdfPreviewUrl" style="user-select: none">
    <input
      type="file"
      ref="fileInput"
      style="display: none"
      @change="onFileChangeAndDrop"
      :accept="accept"
    />
    <div :class="{ _preview_container: !allMiniSize, _preview_container_mini: allMiniSize }">
      <div
        :class="{ _preview_image: !allMiniSize, _preview_image_mini: allMiniSize }"
        @click="openModal('pdf')"
      >
        <div :class="{ _pdf_container: !allMiniSize, _pdf_container_mini: allMiniSize }">
          <div>{{ pdfFileObj?.name ?? pdfTitle }}</div>
          <img :src="pdfIcon" />
        </div>
      </div>
      <div class="_menu_button" v-if="!isNotMenu">
        <CCImageMenuButton
          :uploaded="uploaded"
          :color="color"
          :miniSize="allMiniSize"
          v-model="isMenu"
          opacity
          :showCustomMenu="showCustomMenu"
          @onClick="menuAction"
          @fileChange="fileChange"
        />
      </div>
    </div>

    <Teleport to="body">
      <div v-if="isModalPdf" class="_modal">
        <div class="_modal_background" @click="closeModal('pdf')"></div>
        <embed :src="pdfPreviewUrl ?? props.url" class="_modal_image" width="85%" height="85%" />
        <CCMDIcon name="close" @click="closeModal('pdf')" class="_modal_close" />
      </div>
    </Teleport>
  </div>

  <div v-else-if="videoPreviewUrl" style="user-select: none">
    <input
      type="file"
      ref="fileInput"
      style="display: none"
      @change="onFileChangeAndDrop"
      :accept="accept"
    />
    <div :class="{ _preview_container: !allMiniSize, _preview_container_mini: allMiniSize }">
      <div
        :class="{ _preview_video: !allMiniSize, _preview_video_mini: allMiniSize }"
        class="_video_thumb"
        @click="openModal('video')"
      >
        <video :src="videoPreviewUrl" class="_video_thumb" controls muted></video>
      </div>
      <div class="_menu_button" v-if="!isNotMenu">
        <CCImageMenuButton
          :uploaded="uploaded"
          :color="color"
          :miniSize="allMiniSize"
          v-model="isMenu"
          opacity
          :showCustomMenu="showCustomMenu"
          @onClick="menuAction"
          @fileChange="fileChange"
        />
      </div>
    </div>

    <Teleport to="body">
      <div v-if="isModalVideo" class="_modal">
        <div class="_modal_background" @click="closeModal('video')"></div>
        <video :src="videoPreviewUrl" class="_modal_video" controls></video>
        <CCMDIcon name="close" @click="closeModal('video')" class="_modal_close" />
      </div>
    </Teleport>
  </div>

  <!-- 画像未選択状態 -->
  <div v-else @click="allMiniSize ? chooseFile() : undefined">
    <div
      class="_drop_area"
      :class="{ _drag: isDragOver, _drop_area_mini: allMiniSize }"
      @dragover.prevent="onDragover"
      @dragleave="onDragleave"
      @drop.prevent="onFileChangeAndDrop"
      :style="
        isNotUpload ? { display: 'flex', alignItems: 'center', justifyContent: 'center' } : {}
      "
    >
      <input
        type="file"
        ref="fileInput"
        style="display: none"
        @change="onFileChangeAndDrop"
        :accept="accept"
      />
      <div v-if="!isNotUpload">ドラッグ＆ドロップで<br />ファイルをアップロード</div>
      <CCMDIcon
        name="image_not_supported"
        style="align-items: center"
        :class="{
          _image_not_supported: !allMiniSize,
          _image_not_supported_mini: allMiniSize,
          _not_upload: isNotUpload,
        }"
      />
      <CCButton
        v-if="!allMiniSize"
        @click="chooseFile"
        :label="label"
        :size="size"
        :color="color"
        :square="square"
        :rounded="rounded"
        :outline="outline"
        class="_file_select_button"
        :disabled="disabled"
      />
    </div>
  </div>
</template>

<style lang="sass" scoped>
._drop_area
  user-select: none
  border: 1px solid #707070
  background: #ECECEC
  color: #333333
  font-size: 14px
  padding: 15px 0
  text-align: center
  width: v-bind(imageWidth)
  height: v-bind(imageHeight)
  display: grid
  grid-template-rows: repeat(3, min-content)
  row-gap: 15px
  justify-content: center
  box-sizing: border-box
// miniSizeの場合
._drop_area_mini
  user-select: none
  font-size: 8px
  width: 110px
  height: 75px
  gap: 5px
  ._drag
    border: 1px dashed #707070
._drag
  border: 1px dashed #707070
._preview_image
  width: 100%
  height: 100%
  background: #ECECEC
  object-fit: contain
  cursor: zoom-in
._preview_image_mini
  width: 100%
  height: 100%
  background: #ECECEC
  object-fit: contain
  cursor: zoom-in
._pdf_container
  box-sizing: border-box
  width: v-bind(imageWidth)
  height: v-bind(imageHeight)
  padding: 20px 15px
  text-align: center
  display: flex
  flex-direction: column
  justify-content: space-between
  position: relative
._pdf_container img
  height: 100px
._pdf_container div
  font-size: 14px
  color: #333333
  text-overflow: ellipsis
  white-space: nowrap
  overflow: hidden
._pdf_container_mini
  width: 60px
  height: 100%
  margin: 0 auto
  position: relative
._pdf_container_mini div
  font-size: 8px
  color: #333333
  margin-bottom: 5px
  text-align: center
  line-height: 2
  text-overflow: ellipsis
  white-space: nowrap
  overflow: hidden
._pdf_container_mini img
  width: 60px
  margin: 0 auto
  height: 50px
._image_not_supported
  color: #707070
  font-size: 35px
// miniSizeの場合
._image_not_supported_mini
  font-size: 20px

._file_select_button
  font-size: 14px
  width: 150px
._preview_container
  position: relative
  width: v-bind(imageWidth)
  height: v-bind(imageHeight)
._preview_container_mini
  position: relative
  width: 110px
  height: 75px
  object-fit: cover
._menu_button
  position: absolute
  bottom: 10px
  right: 10px
._modal
  position: fixed
  top: 0
  left: 0
  width: 100vw
  height: 100vh
  background-color: rgba(0, 0, 0, 0.7)
  display: flex
  align-items: center
  justify-content: center
  z-index: 9999999

._modal_background
  position: absolute
  top: 0
  left: 0
  width: 100%
  height: 100%
._modal_image
  z-index: 20
  max-width: 90vw
  max-height: 90vh
  margin: 0 auto
._modal_video
  z-index: 20
  max-width: 90vw
  max-height: 90vh
  margin: 0 auto
._video_thumb
  width: 100%
  height: 100%
  object-fit: contain
._modal_close
  position: absolute
  top: 20px
  right: 20px
  font-size: 40px
  color: #FFFFFF
  cursor: pointer
</style>
