<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { NAlert, NButton, NIcon, NProgress } from 'naive-ui'
import { CloudUpload, Map as MapIcon, X } from '@vicons/tabler'
import MapPickerModal from '@/components/cloud/MapPickerModal.vue'
import { useCloudsStore } from '@/stores/clouds'
import { useUpload } from '@/composables/useUpload'

const props = defineProps<{
  open: boolean
}>()

const emit = defineEmits<{
  close: []
  uploaded: []
}>()

const cloudsStore = useCloudsStore()
const {
  items,
  uploading,
  overallProgress,
  currentItemIndex,
  totalItems,
  addFiles,
  removeItem,
  clearAll,
  validateAll,
  uploadAll,
} = useUpload()

const fileInput = ref<HTMLInputElement | null>(null)
const activeId = ref<string | null>(null)
const dragOver = ref(false)
const errorMsg = ref('')
const successMsg = ref(false)
const mapPickerOpen = ref(false)

const activeItem = computed(() => {
  if (!activeId.value) return null
  return items.value.find(item => item.id === activeId.value) ?? null
})

watch(() => props.open, open => {
  if (open) {
    cloudsStore.fetchCloudTypes()
    errorMsg.value = ''
    successMsg.value = false
  }
})

watch(() => items.value.length, () => {
  if (items.value.length > 0 && !activeId.value) {
    activeId.value = items.value[0].id
  }
  if (items.value.length === 0) {
    activeId.value = null
  }
})

async function addSelectedFiles(files: File[]) {
  const previousLength = items.value.length
  await addFiles(files)
  if (items.value.length > previousLength) {
    activeId.value = items.value[previousLength].id
  }
}

function handleDrop(event: DragEvent) {
  dragOver.value = false
  if (event.dataTransfer?.files) {
    addSelectedFiles(Array.from(event.dataTransfer.files))
  }
}

function handleFileSelect(event: Event) {
  const target = event.target as HTMLInputElement
  if (target.files?.length) {
    addSelectedFiles(Array.from(target.files))
  }
  target.value = ''
}

function handleRemove(id: string) {
  removeItem(id)
  if (activeId.value === id) {
    activeId.value = items.value[0]?.id ?? null
  }
}

function onCategoryChange(event: Event) {
  if (!activeItem.value) return
  const value = (event.target as HTMLSelectElement).value
  if (value === 'other') {
    activeItem.value.cloudCategoryId = 'other'
  } else if (value === '') {
    activeItem.value.cloudCategoryId = null
  } else {
    activeItem.value.cloudCategoryId = Number(value)
  }
  activeItem.value.errors = {}
}

function formatDatetimeLocal(iso: string): string {
  const date = new Date(iso)
  const pad = (value: number) => String(value).padStart(2, '0')
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`
}

function onCapturedAtChange(event: Event) {
  if (!activeItem.value) return
  const value = (event.target as HTMLInputElement).value
  if (!value) return
  activeItem.value.capturedAt = new Date(value).toISOString()
  delete activeItem.value.errors.capturedAt
}

function onLatInput(event: Event) {
  if (!activeItem.value) return
  const value = Number.parseFloat((event.target as HTMLInputElement).value)
  activeItem.value.latitude = Number.isNaN(value) ? null : value
  delete activeItem.value.errors.latitude
  delete activeItem.value.errors.longitude
}

function onLngInput(event: Event) {
  if (!activeItem.value) return
  const value = Number.parseFloat((event.target as HTMLInputElement).value)
  activeItem.value.longitude = Number.isNaN(value) ? null : value
  delete activeItem.value.errors.latitude
  delete activeItem.value.errors.longitude
}

function updatePublicState(isPublic: boolean) {
  if (!activeItem.value) return
  activeItem.value.isHidden = !isPublic
}

function openMapPicker() {
  if (!activeItem.value) return
  mapPickerOpen.value = true
}

function closeMapPicker() {
  mapPickerOpen.value = false
}

function confirmMapPicker(payload: { latitude: number; longitude: number }) {
  if (!activeItem.value) return
  activeItem.value.latitude = payload.latitude
  activeItem.value.longitude = payload.longitude
  delete activeItem.value.errors.latitude
  delete activeItem.value.errors.longitude
  closeMapPicker()
}

async function handleSubmit() {
  errorMsg.value = ''
  successMsg.value = false

  if (!validateAll()) {
    const firstInvalid = items.value.find(item => Object.keys(item.errors).length > 0)
    if (firstInvalid) {
      activeId.value = firstInvalid.id
      await nextTick()
    }
    return
  }

  const result = await uploadAll()
  if (!result.ok) {
    errorMsg.value = '上传失败，请稍后重试'
    return
  }

  successMsg.value = true
  emit('uploaded')
}

function close() {
  if (uploading.value) return
  clearAll()
  errorMsg.value = ''
  successMsg.value = false
  mapPickerOpen.value = false
  emit('close')
}

onBeforeUnmount(() => {
  clearAll()
})
</script>

<template>
  <Teleport to="body">
    <div
      v-if="open"
      class="fixed inset-0 z-[130] flex items-center justify-center bg-slate-950/55 px-4 py-6 backdrop-blur-sm"
      @click="close"
    >
      <div class="w-full max-w-3xl overflow-hidden border border-slate-200 bg-white shadow-2xl" @click.stop>
        <div class="flex items-start justify-between border-b border-slate-200 px-5 py-4">
          <div>
            <p class="text-xs font-semibold uppercase tracking-[0.22em] text-sky-700">Quick Upload</p>
            <h2 class="mt-2 text-xl font-bold text-slate-950">快速上传云图</h2>
            <p class="mt-1 text-sm text-slate-500">从地图当前位置带入经纬度，提交后进入审核队列。</p>
          </div>
          <button
            type="button"
            class="quick-upload-close flex h-9 w-9 items-center justify-center border border-slate-200 bg-slate-50 text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-800"
            aria-label="关闭快捷上传"
            title="关闭快捷上传"
            @click="close"
          >
            <NIcon size="20"><X /></NIcon>
          </button>
        </div>

        <div class="max-h-[72vh] overflow-y-auto px-5 py-5">
          <input ref="fileInput" type="file" accept="image/*" class="hidden" @change="handleFileSelect" />

          <NAlert v-if="successMsg" type="success" :bordered="false" title="已提交审核" class="mb-4">
            图片审核通过后会出现在画廊和地图中。
          </NAlert>

          <div v-if="uploading" class="mb-4 border border-sky-100 bg-sky-50 p-4">
            <div class="mb-2 flex items-center justify-between text-sm">
              <span class="font-medium text-slate-700">正在上传 {{ currentItemIndex }} / {{ totalItems }}</span>
              <span class="font-medium text-sky-700">{{ overallProgress }}%</span>
            </div>
            <NProgress type="line" :percentage="overallProgress" :show-indicator="false" color="#0ea5e9" rail-color="#dbeafe" />
          </div>

          <div
            v-if="items.length === 0"
            class="flex cursor-pointer flex-col items-center justify-center border-2 border-dashed px-6 py-12 text-center transition-colors"
            :class="dragOver ? 'border-sky-400 bg-sky-50' : 'border-slate-300 bg-slate-50 hover:border-sky-300 hover:bg-sky-50'"
            @click="fileInput?.click()"
            @dragover.prevent="dragOver = true"
            @dragleave.prevent="dragOver = false"
            @drop.prevent="handleDrop"
          >
            <NIcon size="42" class="text-sky-600"><CloudUpload /></NIcon>
            <p class="mt-3 text-base font-semibold text-slate-800">选择一张云图</p>
            <p class="mt-1 text-sm text-slate-500">支持 JPG、PNG，可点击或拖拽上传。</p>
          </div>

          <template v-else-if="activeItem">
            <div class="grid gap-5 md:grid-cols-[220px_minmax(0,1fr)]">
              <div>
                <div class="overflow-hidden border border-slate-900 bg-slate-950">
                  <img :src="activeItem.preview" alt="图片预览" class="h-56 w-full object-contain" />
                </div>
                <div class="mt-3 flex items-center justify-between gap-2">
                  <button type="button" class="text-sm font-medium text-sky-700 hover:text-sky-900" @click="fileInput?.click()">重新选择</button>
                  <button type="button" class="text-sm font-medium text-rose-600 hover:text-rose-800" @click="handleRemove(activeItem.id)">移除</button>
                </div>
              </div>

              <div class="space-y-4">
                <div>
                  <label class="mb-1 block text-sm font-medium text-slate-700">类别 <span class="text-rose-500">*</span></label>
                  <select
                    :value="activeItem.cloudCategoryId ?? ''"
                    class="w-full border border-slate-300 bg-white px-3 py-2.5 text-sm text-slate-800 outline-none transition-colors focus:border-sky-500 focus:ring-2 focus:ring-sky-100"
                    @change="onCategoryChange"
                  >
                    <option value="" disabled>请选择</option>
                    <option v-for="cloudType in cloudsStore.cloudTypes" :key="cloudType.id" :value="cloudType.id">
                      {{ cloudType.name }}（{{ cloudType.name_en }}）
                    </option>
                    <option value="other">其他</option>
                  </select>
                  <input
                    v-if="activeItem.cloudCategoryId === 'other'"
                    v-model="activeItem.customCloudType"
                    type="text"
                    placeholder="输入云的类型"
                    class="mt-2 w-full border border-slate-300 px-3 py-2 text-sm outline-none transition-colors focus:border-sky-500 focus:ring-2 focus:ring-sky-100"
                    @input="activeItem.errors = {}"
                  />
                  <p v-if="activeItem.errors.cloudCategory" class="mt-1 text-sm text-rose-600">{{ activeItem.errors.cloudCategory }}</p>
                </div>

                <div>
                  <label class="mb-1 block text-sm font-medium text-slate-700">拍摄时间 <span class="text-rose-500">*</span></label>
                  <input
                    type="datetime-local"
                    :value="formatDatetimeLocal(activeItem.capturedAt)"
                    class="w-full border border-slate-300 px-3 py-2 text-sm outline-none transition-colors focus:border-sky-500 focus:ring-2 focus:ring-sky-100"
                    @change="onCapturedAtChange"
                  />
                  <p v-if="activeItem.errors.capturedAt" class="mt-1 text-sm text-rose-600">{{ activeItem.errors.capturedAt }}</p>
                </div>

                <div>
                  <label class="mb-1 block text-sm font-medium text-slate-700">经纬度</label>
                  <div class="grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_40px] gap-2">
                    <div>
                      <input
                        :value="activeItem.latitude ?? ''"
                        type="number"
                        step="any"
                        placeholder="纬度"
                        class="w-full border border-slate-300 px-3 py-2 text-sm outline-none transition-colors focus:border-sky-500 focus:ring-2 focus:ring-sky-100"
                        @input="onLatInput"
                      />
                      <p v-if="activeItem.errors.latitude" class="mt-1 text-sm text-rose-600">{{ activeItem.errors.latitude }}</p>
                    </div>
                    <div>
                      <input
                        :value="activeItem.longitude ?? ''"
                        type="number"
                        step="any"
                        placeholder="经度"
                        class="w-full border border-slate-300 px-3 py-2 text-sm outline-none transition-colors focus:border-sky-500 focus:ring-2 focus:ring-sky-100"
                        @input="onLngInput"
                      />
                      <p v-if="activeItem.errors.longitude" class="mt-1 text-sm text-rose-600">{{ activeItem.errors.longitude }}</p>
                    </div>
                    <button
                      type="button"
                      class="flex h-10 w-10 items-center justify-center border border-sky-200 bg-sky-50 text-sky-700 transition-colors hover:bg-sky-100"
                      title="地图选点"
                      aria-label="地图选点"
                      @click="openMapPicker"
                    >
                      <NIcon size="20">
                        <MapIcon />
                      </NIcon>
                    </button>
                  </div>
                  <p class="mt-1 text-xs text-slate-400">选填，可手动输入，也可以点击右侧地图按钮选点。</p>
                </div>

                <div>
                  <label class="mb-1 block text-sm font-medium text-slate-700">位置名称</label>
                  <input
                    v-model="activeItem.locationName"
                    type="text"
                    placeholder="如：北京、成都"
                    class="w-full border border-slate-300 px-3 py-2 text-sm outline-none transition-colors focus:border-sky-500 focus:ring-2 focus:ring-sky-100"
                  />
                </div>

                <div>
                  <label class="mb-1 block text-sm font-medium text-slate-700">图片说明</label>
                  <textarea
                    v-model="activeItem.description"
                    rows="3"
                    placeholder="描述一下这张图片..."
                    class="w-full resize-none border border-slate-300 px-3 py-2 text-sm outline-none transition-colors focus:border-sky-500 focus:ring-2 focus:ring-sky-100"
                  ></textarea>
                </div>

                <label class="flex items-center justify-between gap-4 border border-slate-200 bg-slate-50 px-4 py-3">
                  <span>
                    <span class="block text-sm font-medium text-slate-800">公开展示</span>
                    <span class="mt-1 block text-xs text-slate-500">关闭后不会出现在公开地图和画廊中。</span>
                  </span>
                  <input
                    :checked="!activeItem.isHidden"
                    type="checkbox"
                    class="h-5 w-5 border-slate-300 text-sky-600 focus:ring-sky-500"
                    @change="updatePublicState(($event.target as HTMLInputElement).checked)"
                  />
                </label>
              </div>
            </div>

            <NAlert v-if="errorMsg" class="mt-4" type="error" :show-icon="false" title="上传失败">
              {{ errorMsg }}
            </NAlert>
          </template>
        </div>

        <div class="flex items-center justify-end gap-3 border-t border-slate-200 bg-slate-50 px-5 py-4">
          <NButton secondary strong :disabled="uploading" @click="close">关闭</NButton>
          <NButton type="primary" secondary strong :disabled="uploading || items.length === 0" @click="handleSubmit">
            {{ uploading ? '上传中...' : '提交审核' }}
          </NButton>
        </div>
      </div>
    </div>

    <MapPickerModal
      :open="mapPickerOpen"
      :latitude="activeItem?.latitude ?? null"
      :longitude="activeItem?.longitude ?? null"
      description="点击地图即可回填当前图片的经纬度，也可以继续手动修改。"
      footer-text="建议点选大致拍摄位置，上传时会继续做模糊化处理。"
      @close="closeMapPicker"
      @confirm="confirmMapPicker"
    />
  </Teleport>
</template>

<style scoped>
.quick-upload-close.quick-upload-close {
  border-radius: 9999px !important;
}
</style>
