<script setup>
const props = defineProps({
  initialPost: {
    type: Object,
    default: () => ({})
  },
  submitLabel: {
    type: String,
    default: '저장'
  },
  saving: {
    type: Boolean,
    default: false
  }
})

const emit = defineEmits(['submit'])

const autosaveStoragePrefix = 'SORI_ADMIN_POST_AUTOSAVE'
const slugTouched = ref(Boolean(props.initialPost.slug))
const blockEditor = ref(null)
const mediaItems = ref([])
const isMediaPickerOpen = ref(false)
const isLoadingMedia = ref(false)
const isUploadingFeaturedImage = ref(false)
const autosaveTimer = ref(null)
const autosaveNotice = ref(null)
const autosaveStatus = ref('')
const isRestoringAutosave = ref(false)

const form = reactive({
  title: props.initialPost.title || '',
  slug: props.initialPost.slug || '',
  excerpt: props.initialPost.excerpt || '',
  content: props.initialPost.content || '',
  featuredImage: props.initialPost.featuredImage || '',
  status: props.initialPost.status || 'draft',
  tagsText: props.initialPost.tags?.join(', ') || ''
})

const autosaveKey = computed(() => `${autosaveStoragePrefix}:${props.initialPost.id || 'new'}`)

/**
 * 문자열을 URL 슬러그로 변환
 * @param {string} value - 원본 문자열
 * @returns {string} 슬러그
 */
const toSlug = (value) => value
  .trim()
  .toLowerCase()
  .replace(/[^a-z0-9가-힣\s-]/g, '')
  .replace(/\s+/g, '-')
  .replace(/-+/g, '-')
  .replace(/^-|-$/g, '')

watch(() => form.title, (title) => {
  if (!slugTouched.value) {
    form.slug = toSlug(title)
  }
})

/**
 * 슬러그 직접 입력 상태 표시
 * @returns {void}
 */
const touchSlug = () => {
  slugTouched.value = true
  form.slug = toSlug(form.slug)
}

/**
 * 쉼표 구분 태그 문자열을 슬러그 배열로 변환
 * @param {string} value - 태그 입력 문자열
 * @returns {Array<string>} 태그 슬러그 목록
 */
const parseTags = (value) => [...new Set(value
  .split(',')
  .map((tag) => toSlug(tag))
  .filter(Boolean))]

/**
 * 게시물 입력값 생성
 * @returns {Object} 게시물 입력값
 */
const createPostPayload = () => {
  const publishedAt = form.status === 'published'
    ? props.initialPost.publishedAt || new Date().toISOString()
    : null

  return {
    title: form.title.trim(),
    slug: toSlug(form.slug || form.title),
    excerpt: form.excerpt.trim(),
    content: form.content,
    featuredImage: form.featuredImage.trim() || null,
    status: form.status,
    publishedAt,
    tags: parseTags(form.tagsText)
  }
}

/**
 * 자동 저장 데이터 생성
 * @returns {Object} 자동 저장 데이터
 */
const createAutosavePayload = () => ({
  title: form.title,
  slug: form.slug,
  excerpt: form.excerpt,
  content: form.content,
  featuredImage: form.featuredImage,
  status: form.status,
  tagsText: form.tagsText
})

/**
 * 자동 저장 데이터가 비어 있는지 확인
 * @param {Object} payload - 자동 저장 데이터
 * @returns {boolean} 비어 있는지 여부
 */
const isEmptyAutosavePayload = (payload) => ![
  payload.title,
  payload.slug,
  payload.excerpt,
  payload.content,
  payload.featuredImage,
  payload.tagsText
].some((value) => String(value || '').trim())

/**
 * 자동 저장 시각 표시 문자열 반환
 * @param {number} savedAt - 저장 시각
 * @returns {string} 표시 문자열
 */
const formatAutosaveTime = (savedAt) => new Intl.DateTimeFormat('ko-KR', {
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit'
}).format(new Date(savedAt))

/**
 * 자동 저장본을 로컬 저장소에 기록
 * @returns {void}
 */
const saveAutosave = () => {
  if (!import.meta.client || isRestoringAutosave.value) {
    return
  }

  const payload = createAutosavePayload()

  if (isEmptyAutosavePayload(payload)) {
    localStorage.removeItem(autosaveKey.value)
    autosaveStatus.value = ''
    return
  }

  const savedAt = Date.now()
  localStorage.setItem(autosaveKey.value, JSON.stringify({
    savedAt,
    payload
  }))
  autosaveStatus.value = `${formatAutosaveTime(savedAt)} 자동 저장됨`
}

/**
 * 자동 저장 예약
 * @returns {void}
 */
const scheduleAutosave = () => {
  if (!import.meta.client || isRestoringAutosave.value) {
    return
  }

  window.clearTimeout(autosaveTimer.value)
  autosaveTimer.value = window.setTimeout(saveAutosave, 900)
}

/**
 * 자동 저장본을 입력 폼에 복원
 * @returns {void}
 */
const restoreAutosave = () => {
  if (!autosaveNotice.value?.payload) {
    return
  }

  isRestoringAutosave.value = true
  Object.assign(form, autosaveNotice.value.payload)
  slugTouched.value = Boolean(form.slug)
  autosaveStatus.value = `${formatAutosaveTime(autosaveNotice.value.savedAt)} 자동 저장본 복원됨`
  autosaveNotice.value = null

  nextTick(() => {
    isRestoringAutosave.value = false
    scheduleAutosave()
  })
}

/**
 * 자동 저장본을 삭제
 * @returns {void}
 */
const discardAutosave = () => {
  if (!import.meta.client) {
    return
  }

  localStorage.removeItem(autosaveKey.value)
  autosaveNotice.value = null
  autosaveStatus.value = ''
}

/**
 * 미디어 라이브러리 목록 조회
 * @returns {Promise<void>}
 */
const fetchMediaItems = async () => {
  isLoadingMedia.value = true

  try {
    mediaItems.value = await $fetch('/admin/api/media')
  } finally {
    isLoadingMedia.value = false
  }
}

/**
 * 대표 이미지 선택 창 열기
 * @returns {Promise<void>}
 */
const openMediaPicker = async () => {
  isMediaPickerOpen.value = true
  await fetchMediaItems()
}

/**
 * 대표 이미지 선택 창 닫기
 * @returns {void}
 */
const closeMediaPicker = () => {
  isMediaPickerOpen.value = false
}

/**
 * 대표 이미지 선택
 * @param {Object} item - 미디어 항목
 * @returns {void}
 */
const selectFeaturedImage = (item) => {
  form.featuredImage = item.url
  closeMediaPicker()
}

/**
 * 대표 이미지 삭제
 * @returns {void}
 */
const removeFeaturedImage = () => {
  form.featuredImage = ''
}

/**
 * 대표 이미지 파일 업로드
 * @param {Event} event - 파일 입력 이벤트
 * @returns {Promise<void>}
 */
const uploadFeaturedImage = async (event) => {
  const files = event.target.files

  if (!files?.length) {
    return
  }

  const formData = new FormData()
  formData.append('files', files[0])
  isUploadingFeaturedImage.value = true

  try {
    const result = await $fetch('/admin/api/uploads', {
      method: 'POST',
      body: formData
    })
    form.featuredImage = result.files?.[0]?.url || ''
  } finally {
    event.target.value = ''
    isUploadingFeaturedImage.value = false
  }
}

/**
 * 제목 입력 후 본문 에디터로 이동
 * @returns {void}
 */
const focusContentEditor = () => {
  blockEditor.value?.focusFirstBlock()
}

/**
 * 게시물 입력값 제출
 * @returns {void}
 */
const submitPost = () => {
  emit('submit', createPostPayload())
}

watch(form, scheduleAutosave, { deep: true })

onMounted(() => {
  const savedRaw = localStorage.getItem(autosaveKey.value)

  if (!savedRaw) {
    return
  }

  try {
    const saved = JSON.parse(savedRaw)

    if (saved?.payload && !isEmptyAutosavePayload(saved.payload)) {
      autosaveNotice.value = saved
      autosaveStatus.value = `${formatAutosaveTime(saved.savedAt)} 자동 저장본 있음`
    }
  } catch {
    localStorage.removeItem(autosaveKey.value)
  }
})

onBeforeUnmount(() => {
  if (import.meta.client) {
    window.clearTimeout(autosaveTimer.value)
  }
})

defineExpose({
  clearAutosave: discardAutosave
})
</script>

<template>
  <form class="admin-post-form grid gap-6" @submit.prevent="submitPost">
    <div class="admin-post-form__main grid gap-5 lg:grid-cols-[minmax(0,1fr)_18rem]">
      <section class="admin-post-form__content grid gap-4">
        <input
          v-model="form.title"
          class="admin-post-form__title-input border-0 bg-transparent px-0 py-2 text-5xl font-semibold leading-tight text-ink outline-none placeholder:text-soft"
          type="text"
          placeholder="제목"
          required
          @keydown.enter.prevent="focusContentEditor"
        >

        <div
          v-if="autosaveNotice"
          class="admin-post-form__autosave-notice flex flex-wrap items-center justify-between gap-3 rounded border border-line bg-surface px-4 py-3 text-sm"
        >
          <p class="admin-post-form__autosave-message text-muted">
            {{ formatAutosaveTime(autosaveNotice.savedAt) }}에 저장된 작성 중 내용이 있습니다.
          </p>
          <div class="admin-post-form__autosave-actions flex gap-2">
            <button class="admin-post-form__autosave-restore rounded bg-[#15171a] px-3 py-1.5 text-xs font-semibold text-white" type="button" @click="restoreAutosave">
              복원
            </button>
            <button class="admin-post-form__autosave-discard rounded border border-line bg-white px-3 py-1.5 text-xs font-semibold" type="button" @click="discardAutosave">
              삭제
            </button>
          </div>
        </div>

        <div class="admin-post-form__field grid gap-2 text-sm">
          <AdminBlockEditor ref="blockEditor" v-model="form.content" />
        </div>
      </section>

      <aside class="admin-post-form__settings grid content-start gap-4">
        <label class="admin-post-form__field grid gap-2 text-sm">
          <span class="admin-post-form__label font-medium">상태</span>
          <select v-model="form.status" class="admin-post-form__select rounded border border-line bg-white px-3 py-2">
            <option value="draft">초안</option>
            <option value="published">발행</option>
            <option value="private">비공개</option>
          </select>
        </label>

        <label class="admin-post-form__field grid gap-2 text-sm">
          <span class="admin-post-form__label font-medium">슬러그</span>
          <input
            v-model="form.slug"
            class="admin-post-form__input rounded border border-line bg-white px-3 py-2"
            type="text"
            pattern="[a-z0-9가-힣]+(-[a-z0-9가-힣]+)*"
            required
            @input="touchSlug"
          >
        </label>

        <label class="admin-post-form__field grid gap-2 text-sm">
          <span class="admin-post-form__label font-medium">요약</span>
          <textarea
            v-model="form.excerpt"
            class="admin-post-form__textarea min-h-24 rounded border border-line bg-white px-3 py-2"
          />
        </label>

        <label class="admin-post-form__field grid gap-2 text-sm">
          <span class="admin-post-form__label font-medium">태그</span>
          <input
            v-model="form.tagsText"
            class="admin-post-form__input rounded border border-line bg-white px-3 py-2"
            type="text"
          >
        </label>

        <div class="admin-post-form__field grid gap-2 text-sm">
          <span class="admin-post-form__label font-medium">대표 이미지</span>
          <figure v-if="form.featuredImage" class="admin-post-form__featured overflow-hidden rounded border border-line bg-white">
            <img class="admin-post-form__featured-image aspect-[4/3] w-full bg-surface object-cover" :src="form.featuredImage" alt="">
            <figcaption class="admin-post-form__featured-actions grid gap-2 p-3">
              <p class="admin-post-form__featured-url break-all text-xs text-muted">
                {{ form.featuredImage }}
              </p>
              <div class="admin-post-form__featured-buttons flex flex-wrap gap-2">
                <button class="admin-post-form__featured-change rounded border border-line px-3 py-1.5 text-xs font-semibold" type="button" @click="openMediaPicker">
                  변경
                </button>
                <label class="admin-post-form__featured-reupload cursor-pointer rounded border border-line px-3 py-1.5 text-xs font-semibold">
                  새 업로드
                  <input class="sr-only" type="file" accept="image/*" @change="uploadFeaturedImage">
                </label>
                <button class="admin-post-form__featured-remove rounded border border-red-200 px-3 py-1.5 text-xs font-semibold text-red-700" type="button" @click="removeFeaturedImage">
                  삭제
                </button>
              </div>
            </figcaption>
          </figure>
          <div v-else class="admin-post-form__featured-empty grid gap-2 rounded border border-dashed border-line bg-white p-4">
            <button class="admin-post-form__featured-select rounded border border-line px-3 py-2 text-sm font-semibold" type="button" @click="openMediaPicker">
              미디어에서 선택
            </button>
            <label class="admin-post-form__featured-upload cursor-pointer rounded bg-[#15171a] px-3 py-2 text-center text-sm font-semibold text-white">
              {{ isUploadingFeaturedImage ? '업로드 중' : '새 이미지 업로드' }}
              <input class="sr-only" type="file" accept="image/*" @change="uploadFeaturedImage">
            </label>
          </div>
        </div>
      </aside>
    </div>

    <div class="admin-post-form__actions flex justify-end gap-3 border-t border-line pt-5">
      <p v-if="autosaveStatus" class="admin-post-form__autosave-status mr-auto self-center text-xs text-muted">
        {{ autosaveStatus }}
      </p>
      <NuxtLink class="admin-post-form__cancel rounded border border-line bg-white px-4 py-2 text-sm font-semibold" to="/admin/posts">
        취소
      </NuxtLink>
      <button
        class="admin-post-form__submit rounded bg-[#15171a] px-4 py-2 text-sm font-semibold text-white disabled:opacity-50"
        type="submit"
        :disabled="saving"
      >
        {{ saving ? '저장 중' : submitLabel }}
      </button>
    </div>

    <div
      v-if="isMediaPickerOpen"
      class="admin-post-form__media-picker fixed inset-0 z-50 grid place-items-center bg-black/40 px-5 py-8"
      role="dialog"
      aria-modal="true"
      @click.self="closeMediaPicker"
    >
      <section class="admin-post-form__media-picker-panel max-h-[80vh] w-full max-w-4xl overflow-hidden bg-white text-ink shadow-xl">
        <div class="admin-post-form__media-picker-header flex items-center justify-between border-b border-line px-5 py-4">
          <h2 class="admin-post-form__media-picker-title text-lg font-semibold">
            대표 이미지 선택
          </h2>
          <button class="admin-post-form__media-picker-close rounded border border-line px-3 py-1.5 text-sm font-semibold" type="button" @click="closeMediaPicker">
            닫기
          </button>
        </div>
        <div class="admin-post-form__media-picker-body max-h-[62vh] overflow-y-auto p-5">
          <p v-if="isLoadingMedia" class="admin-post-form__media-picker-loading text-sm text-muted">
            미디어를 불러오는 중입니다.
          </p>
          <div v-else-if="mediaItems.length" class="admin-post-form__media-picker-grid grid grid-cols-3 gap-2 sm:grid-cols-4 md:grid-cols-6">
            <button
              v-for="item in mediaItems"
              :key="item.url"
              class="admin-post-form__media-picker-item overflow-hidden border border-line bg-white text-left"
              type="button"
              @click="selectFeaturedImage(item)"
            >
              <img class="admin-post-form__media-picker-image aspect-square w-full bg-surface object-cover" :src="item.url" :alt="item.title">
              <span class="admin-post-form__media-picker-name block truncate px-2 py-1.5 text-xs font-semibold text-ink">{{ item.name }}</span>
            </button>
          </div>
          <p v-else class="admin-post-form__media-picker-empty border border-dashed border-line p-8 text-center text-sm text-muted">
            선택할 미디어가 없습니다.
          </p>
        </div>
      </section>
    </div>
  </form>
</template>
