<script setup>
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import Editor from '@toast-ui/editor'
import '@toast-ui/editor/dist/toastui-editor.css'
import codeSyntaxHighlight from '@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight-all'
import 'prismjs/themes/prism-tomorrow.css'
import '@toast-ui/editor-plugin-code-syntax-highlight/dist/toastui-editor-plugin-code-syntax-highlight.css'
import { imageToBlob } from '@/utils/receiptOcr'
import { boardApi } from '@/api/boardApi'

const props = defineProps({
  modelValue: { type: String, default: '' },
  height: { type: String, default: '400px' },
  placeholder: { type: String, default: '' },
  initialEditType: { type: String, default: 'wysiwyg' }, // 'markdown' | 'wysiwyg'
})
const emit = defineEmits(['update:modelValue'])

// PC(Electron)는 Toast UI 에디터가 Windows IME 와 충돌(첫 자음 중복)하므로 textarea 기반 에디터를 쓴다.
// 웹/모바일(APK)은 Toast UI 위지윅 유지.
const isElectron = typeof navigator !== 'undefined' && /electron/i.test(navigator.userAgent)

// 공용: 이미지 압축 후 서버 업로드 → URL 반환
async function uploadCompressed(fileOrBlob) {
  const blob = await imageToBlob(fileOrBlob, 1280) // 긴 변 1280px, JPEG 0.85
  const { url } = await boardApi.uploadImage(blob)
  return url
}

/* ===================== PC: 본문 텍스트(textarea) + 이미지 첨부(썸네일) ===================== */
// 이미지 마크다운은 본문 텍스트에 노출하지 않고 별도 첨부로 관리 → 저장 시 본문 끝에 합쳐 emit.
const IMG_RE = /!\[([^\]]*)\]\(\s*([^)\s]+)[^)]*\)/g
function splitContent(md) {
  const imgs = []
  const body = (md || '')
    .replace(IMG_RE, (_, alt, url) => {
      imgs.push({ url, alt: alt || '' })
      return ''
    })
    .replace(/\n{3,}/g, '\n\n')
    .trim()
  return { body, imgs }
}
const _init = splitContent(props.modelValue)
const bodyText = ref(_init.body)
const images = ref(_init.imgs) // [{ url, alt }]
const taRef = ref(null)
const imgInput = ref(null)
const imgBusy = ref(false)
const imgMsg = ref('')
let imgMsgTimer = null
function flashImgMsg(msg) {
  imgMsg.value = msg
  if (imgMsgTimer) clearTimeout(imgMsgTimer)
  imgMsgTimer = setTimeout(() => (imgMsg.value = ''), 4000)
}

function combine() {
  const imgMd = images.value.map((i) => `![${i.alt || ''}](${i.url})`).join('\n\n')
  return [bodyText.value.trim(), imgMd].filter(Boolean).join('\n\n')
}

let lastEmitted = isElectron ? combine() : (props.modelValue || '')

if (isElectron) {
  watch(
    [bodyText, images],
    () => {
      lastEmitted = combine()
      emit('update:modelValue', lastEmitted)
    },
    { deep: true },
  )
  watch(
    () => props.modelValue,
    (v) => {
      if ((v || '') === lastEmitted) return
      const sp = splitContent(v)
      bodyText.value = sp.body
      images.value = sp.imgs
      lastEmitted = v || ''
    },
  )
}

// 선택 영역을 마커로 감싸기(굵게/기울임/코드 등)
function surround(before, after = before, placeholder = '') {
  const ta = taRef.value
  if (!ta) return
  const s = ta.selectionStart
  const e = ta.selectionEnd
  const val = bodyText.value
  const sel = val.slice(s, e) || placeholder
  bodyText.value = val.slice(0, s) + before + sel + after + val.slice(e)
  nextTick(() => {
    ta.focus()
    const start = s + before.length
    ta.setSelectionRange(start, start + sel.length)
  })
}
// 선택한 줄들 앞에 접두어 추가(제목/인용/목록 등)
function linePrefix(prefix) {
  const ta = taRef.value
  if (!ta) return
  const s = ta.selectionStart
  const e = ta.selectionEnd
  const val = bodyText.value
  const lineStart = val.lastIndexOf('\n', s - 1) + 1
  const block = val.slice(lineStart, e)
  const prefixed = block
    .split('\n')
    .map((ln) => prefix + ln)
    .join('\n')
  bodyText.value = val.slice(0, lineStart) + prefixed + val.slice(e)
  nextTick(() => {
    ta.focus()
    ta.setSelectionRange(lineStart, lineStart + prefixed.length)
  })
}
// 현재 위치에 블록 삽입(코드블록/구분선)
function insertBlock(block) {
  const ta = taRef.value
  if (!ta) return
  const s = ta.selectionStart
  const val = bodyText.value
  const nl = s > 0 && val[s - 1] !== '\n' ? '\n' : ''
  const insert = nl + block + '\n'
  bodyText.value = val.slice(0, s) + insert + val.slice(s)
  nextTick(() => {
    ta.focus()
    const pos = s + insert.length
    ta.setSelectionRange(pos, pos)
  })
}
function insertLink() {
  const ta = taRef.value
  if (!ta) return
  const s = ta.selectionStart
  const e = ta.selectionEnd
  const val = bodyText.value
  const sel = val.slice(s, e) || '링크텍스트'
  const md = `[${sel}](url)`
  bodyText.value = val.slice(0, s) + md + val.slice(e)
  nextTick(() => {
    ta.focus()
    const urlStart = s + sel.length + 3
    ta.setSelectionRange(urlStart, urlStart + 3)
  })
}

// 이미지: 파일 → 압축 → 서버 업로드 → 첨부 목록에 추가(본문 텍스트엔 미노출)
function pickImage() {
  imgInput.value?.click()
}
async function onImagePick(e) {
  const file = e.target.files?.[0]
  e.target.value = ''
  if (!file) return
  imgBusy.value = true
  imgMsg.value = '이미지 업로드 중…'
  try {
    const url = await uploadCompressed(file)
    const alt = file.name.replace(/\.[^.]+$/, '')
    images.value = [...images.value, { url, alt }]
    imgMsg.value = ''
  } catch (err) {
    flashImgMsg(err?.response?.data?.message || '이미지 업로드에 실패했습니다.')
  } finally {
    imgBusy.value = false
  }
}
function removeImage(i) {
  images.value = images.value.filter((_, idx) => idx !== i)
}

/* ===================== 웹/APK: Toast UI 위지윅 ===================== */
const el = ref(null)
let editor = null

onMounted(() => {
  if (isElectron) return
  editor = new Editor({
    el: el.value,
    height: props.height,
    initialEditType: props.initialEditType,
    hideModeSwitch: true,
    plugins: [codeSyntaxHighlight],
    initialValue: props.modelValue || '',
    placeholder: props.placeholder,
    usageStatistics: false,
    autofocus: false,
    events: {
      change: () => {
        lastEmitted = editor.getMarkdown()
        emit('update:modelValue', lastEmitted)
      },
    },
    hooks: {
      // 이미지 추가 시 base64 대신 서버 업로드 → URL 삽입
      addImageBlobHook: async (blob, callback) => {
        try {
          const url = await uploadCompressed(blob)
          callback(url, '')
        } catch {
          /* 업로드 실패 시 미삽입 */
        }
        return false
      },
    },
  })
})

watch(
  () => props.modelValue,
  (val) => {
    if (isElectron) return
    const v = val || ''
    if (v === lastEmitted) return
    if (editor && v !== editor.getMarkdown()) {
      lastEmitted = v
      editor.setMarkdown(v)
    }
  },
)

onBeforeUnmount(() => {
  editor?.destroy()
  editor = null
})
</script>

<template>
  <!-- PC(Electron): textarea 기반 마크다운 에디터 (툴바 + 이미지 첨부 썸네일) — 한글 IME 정상 -->
  <div v-if="isElectron" class="mde">
    <div class="mde-toolbar">
      <button type="button" title="제목" @click="linePrefix('## ')"><span class="t-h">H</span></button>
      <button type="button" title="굵게" @click="surround('**', '**', '굵게')"><b>B</b></button>
      <button type="button" title="기울임" @click="surround('*', '*', '기울임')"><i>I</i></button>
      <button type="button" title="취소선" @click="surround('~~', '~~', '취소선')"><s>S</s></button>
      <span class="mde-div"></span>
      <button type="button" title="인용" @click="linePrefix('&gt; ')">❝</button>
      <button type="button" title="목록" @click="linePrefix('- ')">•</button>
      <button type="button" title="번호 목록" @click="linePrefix('1. ')">1.</button>
      <button type="button" title="체크리스트" @click="linePrefix('- [ ] ')">☑</button>
      <span class="mde-div"></span>
      <button type="button" title="링크" @click="insertLink()">🔗</button>
      <button type="button" :title="imgBusy ? '이미지 처리 중…' : '이미지 첨부'" :disabled="imgBusy" @click="pickImage()">🖼</button>
      <button type="button" title="인라인 코드" @click="surround('`', '`', '코드')">&lt;/&gt;</button>
      <button type="button" title="코드 블록" @click="insertBlock('```\n코드\n```')">{ }</button>
      <button type="button" title="구분선" @click="insertBlock('---')">―</button>
      <input ref="imgInput" type="file" accept="image/*" hidden @change="onImagePick" />
    </div>
    <p v-if="imgMsg" class="mde-imgmsg">{{ imgMsg }}</p>
    <!-- 첨부 이미지 썸네일 — 본문엔 텍스트만, 이미지는 여기서 확인/삭제 -->
    <div v-if="images.length" class="mde-thumbs">
      <div v-for="(img, i) in images" :key="i" class="mde-thumb-wrap">
        <img :src="img.url" class="mde-thumb" alt="" :title="img.alt || img.url" />
        <button type="button" class="mde-thumb-del" title="이미지 제거" @click="removeImage(i)">×</button>
      </div>
    </div>
    <textarea
      ref="taRef"
      v-model="bodyText"
      class="mde-textarea"
      :placeholder="placeholder"
      :style="{ height }"
    ></textarea>
  </div>

  <!-- 웹/APK: Toast UI 위지윅 -->
  <div v-else ref="el"></div>
</template>

<style scoped>
.mde {
  border: 1px solid var(--color-border);
  border-radius: 6px;
  overflow: hidden;
  background: var(--color-background-soft);
}
.mde-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.15rem;
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid var(--color-border);
  background: var(--color-background);
}
.mde-toolbar button {
  min-width: 1.9rem;
  height: 1.9rem;
  padding: 0 0.4rem;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--color-text);
  font-size: 0.9rem;
  cursor: pointer;
  line-height: 1;
}
.mde-toolbar button:hover {
  background: var(--color-background-mute);
}
.mde-toolbar .t-h {
  font-weight: 800;
}
.mde-div {
  width: 1px;
  height: 1.1rem;
  margin: 0 0.25rem;
  background: var(--color-border);
}
.mde-imgmsg {
  margin: 0;
  padding: 0.4rem 0.9rem;
  font-size: 0.82rem;
  color: hsla(160, 100%, 32%, 1);
  background: hsla(160, 100%, 37%, 0.08);
  border-bottom: 1px solid var(--color-border);
}
.mde-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  padding: 0.6rem;
  border-bottom: 1px solid var(--color-border);
  background: var(--color-background);
}
.mde-thumb-wrap {
  position: relative;
  flex: 0 0 auto;
}
.mde-thumb {
  display: block;
  height: 120px;
  max-width: 220px;
  object-fit: cover;
  border: 1px solid var(--color-border);
  border-radius: 6px;
}
.mde-thumb-del {
  position: absolute;
  top: 5px;
  right: 5px;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
}
.mde-thumb-del:hover {
  background: rgba(0, 0, 0, 0.78);
}
.mde-textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 0.8rem 0.9rem;
  border: 0;
  background: var(--color-background);
  color: var(--color-text);
  font-family: inherit;
  font-size: 0.95rem;
  line-height: 1.7;
  resize: vertical;
}
.mde-textarea:focus {
  outline: none;
}
</style>
