<script setup>
const props = defineProps({
  initialPage: {
    type: Object,
    default: () => ({})
  },
  saving: {
    type: Boolean,
    default: false
  },
  deleting: {
    type: Boolean,
    default: false
  },
  canViewPage: {
    type: Boolean,
    default: false
  },
  publicUrl: {
    type: String,
    default: ''
  },
  showDelete: {
    type: Boolean,
    default: false
  }
})

const emit = defineEmits(['submit', 'delete'])

const slugTouched = ref(Boolean(props.initialPage.slug))
const blockEditor = ref(null)
const htmlEditor = ref(null)
const editorMode = ref('write')
const isUploadingPageAsset = ref(false)
const isSettingsOpen = ref(true)
const savedPageSnapshot = ref('')
const htmlCursorRange = reactive({
  start: 0,
  end: 0
})

const form = reactive({
  title: props.initialPage.title || '',
  slug: props.initialPage.slug || '',
  renderMode: props.initialPage.renderMode || 'html_document',
  content: props.initialPage.content || ''
})

/**
 * 한글 음절 1자를 영문 표기로 변환
 * @param {string} char - 변환할 문자
 * @returns {string} 영문 표기
 */
const romanizeHangulSyllable = (char) => {
  const syllableCode = char.charCodeAt(0)
  const hangulBase = 0xac00
  const hangulLast = 0xd7a3

  if (syllableCode < hangulBase || syllableCode > hangulLast) {
    return char
  }

  const choseong = ['g', 'kk', 'n', 'd', 'tt', 'r', 'm', 'b', 'pp', 's', 'ss', '', 'j', 'jj', 'ch', 'k', 't', 'p', 'h']
  const jungseong = ['a', 'ae', 'ya', 'yae', 'eo', 'e', 'yeo', 'ye', 'o', 'wa', 'wae', 'oe', 'yo', 'u', 'wo', 'we', 'wi', 'yu', 'eu', 'ui', 'i']
  const jongseong = ['', 'k', 'k', 'ks', 'n', 'nj', 'nh', 't', 'l', 'lk', 'lm', 'lb', 'ls', 'lt', 'lp', 'lh', 'm', 'p', 'ps', 't', 't', 'ng', 't', 't', 'k', 't', 'p', 'h']

  const offset = syllableCode - hangulBase
  const choseongIndex = Math.floor(offset / 588)
  const jungseongIndex = Math.floor((offset % 588) / 28)
  const jongseongIndex = offset % 28

  return `${choseong[choseongIndex]}${jungseong[jungseongIndex]}${jongseong[jongseongIndex]}`
}

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

const pageSlug = computed(() => toSlug(form.slug || form.title))
const viewPageUrl = computed(() => props.publicUrl || (pageSlug.value ? `/pages/${pageSlug.value}` : ''))
const pageUrlHint = computed(() => `/pages/${pageSlug.value || 'page-slug'}/`)

/**
 * 페이지 폼의 저장 비교용 문자열을 생성한다.
 * @returns {string} 직렬화된 폼 상태
 */
const serializePageForm = () => JSON.stringify({
  title: form.title.trim(),
  slug: pageSlug.value,
  renderMode: form.renderMode,
  content: form.content
})

const hasUnsavedPageChanges = computed(() => serializePageForm() !== savedPageSnapshot.value)

const headerStatusText = computed(() => {
  if (props.saving) {
    return 'Saving...'
  }
  if (hasUnsavedPageChanges.value) {
    return 'Unsaved changes'
  }
  if (props.initialPage.id) {
    return 'Saved'
  }
  return 'New page'
})

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

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

/**
 * 페이지 설정 패널을 토글한다.
 * @returns {void}
 */
const toggleSettingsPanel = () => {
  isSettingsOpen.value = !isSettingsOpen.value
}

/**
 * 제목 입력 후 본문 에디터로 이동
 * @param {KeyboardEvent} event - 키보드 이벤트
 * @returns {void}
 */
const focusContentEditor = (event) => {
  event?.preventDefault()

  if (form.renderMode === 'html_document') {
    htmlEditor.value?.focus()
    return
  }

  blockEditor.value?.focusFirstBlock()
}

/**
 * 페이지 작성 모드를 변경한다.
 * @param {'markdown'|'html_document'} mode - 페이지 작성 모드
 * @returns {void}
 */
const setRenderMode = (mode) => {
  form.renderMode = mode
}

/**
 * HTML textarea 커서 위치를 기억한다.
 * @returns {void}
 */
const rememberHtmlCursor = () => {
  if (!htmlEditor.value) {
    return
  }

  htmlCursorRange.start = htmlEditor.value.selectionStart ?? form.content.length
  htmlCursorRange.end = htmlEditor.value.selectionEnd ?? htmlCursorRange.start
}

/**
 * HTML 본문 커서 위치에 텍스트를 삽입한다.
 * @param {string} text - 삽입할 텍스트
 * @returns {Promise<void>}
 */
const insertTextAtHtmlCursor = async (text) => {
  const start = Math.max(0, htmlCursorRange.start)
  const end = Math.max(start, htmlCursorRange.end)

  form.content = `${form.content.slice(0, start)}${text}${form.content.slice(end)}`

  await nextTick()

  const nextCursor = start + text.length
  htmlEditor.value?.focus()
  htmlEditor.value?.setSelectionRange(nextCursor, nextCursor)
  htmlCursorRange.start = nextCursor
  htmlCursorRange.end = nextCursor
}

/**
 * 페이지 HTML 자산을 업로드하고 본문 커서 위치에 URL을 삽입한다.
 * @param {Event} event - 파일 입력 이벤트
 * @returns {Promise<void>}
 */
const uploadPageAsset = async (event) => {
  const files = event.target.files

  if (!files?.length) {
    return
  }

  rememberHtmlCursor()

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

  try {
    const result = await $fetch('/admin/api/uploads', {
      method: 'POST',
      body: formData
    })
    const uploadedUrl = result.files?.[0]?.url || ''

    if (uploadedUrl && form.renderMode === 'html_document') {
      await insertTextAtHtmlCursor(uploadedUrl)
    }
  } finally {
    event.target.value = ''
    isUploadingPageAsset.value = false
  }
}

/**
 * 페이지 입력값을 생성한다.
 * @returns {Object} 페이지 입력값
 */
const createPayload = () => ({
  title: form.title.trim(),
  slug: pageSlug.value,
  renderMode: form.renderMode,
  content: form.content,
  featuredImage: null
})

/**
 * 페이지 입력값 제출
 * @returns {void}
 */
const submitPage = () => {
  emit('submit', createPayload())
}

/**
 * 현재 폼 상태를 저장 완료 기준점으로 표시한다.
 * @returns {void}
 */
const markSaved = () => {
  savedPageSnapshot.value = serializePageForm()
}

onMounted(markSaved)

defineExpose({
  markSaved
})
</script>

<template>
  <form class="admin-page-form flex h-screen min-h-screen overflow-hidden bg-white" @submit.prevent="submitPage">
    <div class="admin-page-form__workspace flex min-w-0 flex-1 flex-col bg-white">
      <header class="admin-page-form__toolbar flex h-[56px] shrink-0 items-center bg-white px-8">
        <div class="admin-page-form__toolbar-inner flex h-[34px] min-w-0 flex-1 items-center justify-between">
          <div class="admin-page-form__toolbar-left flex h-full min-w-0 flex-1 items-center gap-3">
            <NuxtLink class="admin-page-form__toolbar-link inline-flex shrink-0 items-center gap-2 rounded px-2 py-1.5 text-sm font-medium text-[#394047] transition-colors hover:bg-[#f1f3f4] hover:text-black" to="/admin/pages">
              <span class="admin-page-form__toolbar-back text-lg leading-none" aria-hidden="true">&lt;</span>
              <span>Pages</span>
            </NuxtLink>
            <NuxtLink
              v-if="canViewPage && viewPageUrl"
              class="admin-page-form__toolbar-status-link inline-flex items-center gap-1 truncate rounded px-2 py-1.5 text-sm font-medium text-[#8E9CAC] transition-colors hover:bg-[#f1f3f4] hover:text-[#394047]"
              :to="viewPageUrl"
              target="_blank"
              rel="noopener noreferrer"
            >
              <span>View page</span>
              <span aria-hidden="true">↗</span>
            </NuxtLink>
            <span v-else class="admin-page-form__toolbar-status truncate rounded px-2 py-1.5 text-sm text-[#8E9CAC]">
              {{ headerStatusText }}
            </span>
          </div>

          <div class="admin-page-form__toolbar-actions flex h-full shrink-0 items-center gap-2">
            <div
              v-show="form.renderMode === 'markdown'"
              id="admin-page-form-mode-toggle-host"
              class="admin-page-form__mode-toggle-host flex shrink-0 items-center"
            />
            <button
              class="admin-page-form__toolbar-save rounded px-3 py-1.5 text-sm font-bold transition-colors disabled:cursor-default disabled:text-[#8E9CAC] disabled:hover:bg-transparent enabled:text-[#394047] enabled:hover:bg-[#f1f3f4]"
              type="submit"
              :disabled="saving || !form.title.trim() || !hasUnsavedPageChanges"
            >
              {{ saving ? 'Saving...' : 'Save' }}
            </button>
            <button
              class="admin-page-form__settings-toggle grid size-[34px] place-items-center rounded text-[#394047] transition-colors hover:bg-[#f1f3f4] hover:text-black"
              type="button"
              :aria-pressed="isSettingsOpen"
              aria-label="페이지 설정 패널 전환"
              @click="toggleSettingsPanel"
            >
              <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">
                <path d="M14 2.16699C14.3242 2.16699 14.4998 2.39365 14.5 2.57129V13.4287C14.5 13.606 14.3242 13.834 14 13.834H11.5V2.16699H14ZM2 2.16699H10.5V13.834H2C1.6756 13.834 1.5 13.6064 1.5 13.4287V2.57129C1.50024 2.39409 1.67607 2.16699 2 2.16699Z" stroke="currentColor" />
              </svg>
            </button>
          </div>
        </div>
      </header>

      <main class="admin-page-form__editor-scroll min-h-0 flex-1 overflow-y-auto">
        <section class="admin-page-form__content mx-auto w-full max-w-[804px] px-8 pb-16 pt-24">
          <input
            v-model="form.title"
            class="admin-page-form__title-input mb-2 w-full border-0 bg-transparent px-0 py-0 text-3xl font-bold leading-tight text-ink outline-none placeholder:text-[#8e9cac]"
            type="text"
            placeholder="제목"
            @keydown.enter="focusContentEditor"
          >

          <div v-if="form.renderMode === 'markdown'" class="admin-page-form__field admin-page-form__content-editor text-sm">
            <AdminMarkdownEditor
              ref="blockEditor"
              v-model="form.content"
              v-model:editor-mode="editorMode"
              mode-toggle-teleport-to="#admin-page-form-mode-toggle-host"
            />
          </div>
          <label v-else class="admin-page-form__field admin-page-form__html-field grid gap-2 text-sm">
            <span class="admin-page-form__label sr-only">HTML 문서</span>
            <textarea
              ref="htmlEditor"
              v-model="form.content"
              class="admin-page-form__html-editor min-h-[68vh] w-full resize-y rounded border border-[#e3e6e8] bg-white px-4 py-4 font-mono text-sm leading-6 text-[#15171a] outline-none placeholder:text-[#8e9cac] focus:border-[#8e9cac]"
              spellcheck="false"
              @blur="rememberHtmlCursor"
              @click="rememberHtmlCursor"
              @focus="rememberHtmlCursor"
              @input="rememberHtmlCursor"
              @keyup="rememberHtmlCursor"
              @select="rememberHtmlCursor"
              placeholder="<!doctype html>
<html lang=&quot;ko&quot;>
<head>
  <meta charset=&quot;utf-8&quot;>
  <title>Landing</title>
  <style>
    body { margin: 0; }
  </style>
</head>
<body>
</body>
</html>"
            />
          </label>
        </section>
      </main>
    </div>

    <aside
      class="admin-page-form__settings flex h-screen shrink-0 flex-col overflow-hidden border-[#e3e6e8] bg-white transition-[width,border-color] duration-300 ease-out"
      :class="isSettingsOpen ? 'w-[420px] border-l' : 'w-0 border-l-0'"
      :aria-hidden="!isSettingsOpen"
    >
      <div class="admin-page-form__settings-inner relative flex h-full w-[420px] flex-col">
        <div class="admin-page-form__settings-header flex h-[56px] shrink-0 items-center justify-between px-6">
          <h2 class="admin-page-form__settings-title text-xl font-bold text-black">
            페이지 설정
          </h2>
          <button class="admin-page-form__settings-close grid size-8 place-items-center rounded text-neutral-900 transition-colors hover:bg-[#eff1f2] hover:text-neutral-500" type="button" aria-label="페이지 설정 닫기" @click="toggleSettingsPanel">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="16" width="16" class="h-[1.1rem] w-[1.1rem]" aria-hidden="true">
              <path stroke-linecap="round" stroke-width="0.4" fill="currentColor" stroke="#000000" stroke-linejoin="round" d="M.44,21.44a1.49,1.49,0,0,0,0,2.12,1.5,1.5,0,0,0,2.12,0l9.26-9.26a.25.25,0,0,1,.36,0l9.26,9.26a1.5,1.5,0,0,0,2.12,0,1.49,1.49,0,0,0,0-2.12L14.3,12.18a.25.25,0,0,1,0-.36l9.26-9.26A1.5,1.5,0,0,0,21.44.44L12.18,9.7a.25.25,0,0,1-.36,0L2.56.44A1.5,1.5,0,0,0,.44,2.56L9.7,11.82a.25.25,0,0,1,0,.36Z" />
            </svg>
          </button>
        </div>

        <div class="admin-page-form__settings-body grid flex-1 content-start gap-4 overflow-y-auto px-6 pb-8 pt-8">
          <div class="admin-page-form__field grid gap-1 text-sm">
            <div class="admin-page-form__page-url-header flex h-[22px] items-center justify-between">
              <span class="admin-page-form__label font-bold text-[#15171a]">Page URL</span>
              <NuxtLink
                v-if="canViewPage && viewPageUrl"
                class="admin-page-form__view-page inline-flex items-center gap-1 rounded px-3 py-1.5 text-xs text-[#8e9cac] transition-colors hover:bg-[#eff1f2] hover:text-[#394047]"
                :to="viewPageUrl"
                target="_blank"
              >
                <span>View Page</span>
                <span aria-hidden="true">↗</span>
              </NuxtLink>
            </div>
            <label class="admin-page-form__page-url-input flex h-[38px] items-center gap-2 rounded border border-[#e3e6e8] bg-[#eff1f2] px-3 py-1 transition-colors hover:border-[#c8ced3] focus-within:border-[#8e9cac]">
              <span class="admin-page-form__page-url-icon text-sm text-[#394047]" aria-hidden="true">⌘</span>
              <input
                v-model="form.slug"
                class="admin-page-form__input min-w-0 flex-1 border-0 bg-transparent p-0 text-sm text-black outline-none"
                type="text"
                pattern="[a-z0-9]+(-[a-z0-9]+)*"
                required
                @input="touchSlug"
              >
            </label>
            <p class="admin-page-form__page-url-hint text-xs text-[#7c8b9a]">
              {{ pageUrlHint }}
            </p>
          </div>

          <div v-if="form.renderMode === 'html_document'" class="admin-page-form__field grid gap-2 text-sm">
            <span class="admin-page-form__label font-medium">페이지 형식</span>
            <div class="admin-page-form__mode-control grid grid-cols-2 rounded border border-[#e3e6e8] bg-[#eff1f2] p-1">
              <button
                class="admin-page-form__mode-button rounded px-3 py-2 text-sm font-semibold transition"
                :class="form.renderMode === 'markdown' ? 'bg-[#15171a] text-white' : 'text-[#7c8b9a] hover:bg-white hover:text-[#15171a]'"
                type="button"
                @click="setRenderMode('markdown')"
              >
                일반 텍스트
              </button>
              <button
                class="admin-page-form__mode-button rounded px-3 py-2 text-sm font-semibold transition"
                :class="form.renderMode === 'html_document' ? 'bg-[#15171a] text-white' : 'text-[#7c8b9a] hover:bg-white hover:text-[#15171a]'"
                type="button"
                @click="setRenderMode('html_document')"
              >
                HTML
              </button>
            </div>
          </div>

          <div class="admin-page-form__field grid gap-2 text-sm">
            <span class="admin-page-form__label font-medium">HTML 자산</span>
            <label
              class="admin-page-form__asset-upload inline-flex h-10 cursor-pointer items-center justify-center rounded bg-[#15171a] px-3 text-sm font-semibold text-white transition-colors hover:bg-black"
              :class="{ 'pointer-events-none opacity-50': isUploadingPageAsset }"
            >
              {{ isUploadingPageAsset ? '업로드 중' : '파일 업로드' }}
              <input
                class="sr-only"
                type="file"
                accept="image/*,video/*,audio/*,.pdf,.zip,.txt,.csv,.docx,.xlsx,.pptx"
                @change="uploadPageAsset"
              >
            </label>
            <p class="admin-page-form__asset-upload-hint text-xs leading-5 text-[#7c8b9a]">
              HTML 모드에서는 업로드된 파일 URL을 현재 커서 위치에 삽입합니다. 예: &lt;img src=&quot;여기&quot;&gt;
            </p>
          </div>
        </div>

        <div v-if="showDelete" class="admin-page-form__settings-footer border-t border-[#e3e6e8] p-6">
          <button
            class="admin-page-form__delete-button flex h-[44px] w-full items-center justify-center gap-2 rounded border border-[#d7dce0] bg-white text-sm font-bold text-[#394047] transition-colors hover:border-red-200 hover:bg-red-50 hover:text-red-700 disabled:opacity-50"
            type="button"
            :disabled="deleting"
            @click="emit('delete')"
          >
            <svg class="size-4" viewBox="0 0 24 24" fill="none" aria-hidden="true">
              <path d="M4 7h16M10 11v6M14 11v6M6 7l1 14h10l1-14M9 7V4h6v3" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.7" />
            </svg>
            <span>{{ deleting ? 'Deleting page' : 'Delete page' }}</span>
          </button>
        </div>
      </div>
    </aside>

  </form>
</template>
