import { nextTick } from 'vue'
import Sortable from 'sortablejs'

export function useAdminTemplateManager({
  api,
  toApiUrl,
  selectedTemplateId,
  selectedTemplate,
  uploadFiles,
  uploadItemDrafts,
  thumbFile,
  itemPreviewUrls,
  itemFileInput,
  templateItemListEl,
  templateItemSortable,
  savedTemplateItemOrderIds,
  isTemplateLoading,
  activeTemplateRequest,
  templateRequests,
  customItemModalOpen,
  customItemModalTargetTemplateId,
  newTemplateId,
  newTemplateName,
  newTemplateIsPublic,
  clearPreviewUrl,
  resetFileInput,
  resetUploadState,
  refreshTemplates,
  closeTemplateCreateModal,
  resetMessages,
  success,
  error,
}) {
  function parseTagsText(value) {
    return Array.from(
      new Set(
        String(value || '')
          .split(',')
          .map((tag) => tag.trim().replace(/^#/, '').slice(0, 40))
          .filter(Boolean)
      )
    ).slice(0, 30)
  }

  function normalizeDraftSrc(src) {
    if (typeof src !== 'string') return ''
    const raw = src.trim()
    if (!raw) return ''
    if (raw.startsWith('/uploads/')) return raw
    try {
      const url = new URL(raw)
      return url.pathname || raw
    } catch (e) {
      return raw
    }
  }

  function requestItemFilename(item = {}) {
    const src = typeof item.src === 'string' ? item.src : ''
    return src.split('/').pop() || item.file?.name || 'item'
  }

  function destroyTemplateItemSortable() {
    if (templateItemSortable.value) {
      templateItemSortable.value.destroy()
      templateItemSortable.value = null
    }
  }

  async function syncTemplateItemSortable() {
    await nextTick()
    destroyTemplateItemSortable()
    if (!templateItemListEl.value || !selectedTemplate.value?.items?.length) return

    templateItemSortable.value = Sortable.create(templateItemListEl.value, {
      animation: 160,
      draggable: '[data-template-item-id]',
      forceFallback: true,
      fallbackOnBody: false,
      filter: '[data-no-drag]',
      preventOnFilter: false,
      fallbackClass: 'thumbCard--dragging',
      ghostClass: 'ghost',
      chosenClass: 'chosen',
      onEnd: (evt) => {
        if (evt.oldIndex == null || evt.newIndex == null || evt.oldIndex === evt.newIndex) return
        const nextItems = [...(selectedTemplate.value?.items || [])]
        const [moved] = nextItems.splice(evt.oldIndex, 1)
        nextItems.splice(evt.newIndex, 0, moved)
        selectedTemplate.value = {
          ...selectedTemplate.value,
          items: nextItems,
        }
      },
    })
  }

  function mergeRequestItemsIntoDrafts(request) {
    const requestId = request?.id
    if (!requestId) return
    const existingTemplateSrcs = new Set((selectedTemplate.value?.items || []).map((item) => normalizeDraftSrc(item?.src)).filter(Boolean))
    const existingKeys = new Set(uploadItemDrafts.value.map((draft) => `${draft.kind}:${draft.requestId || ''}:${draft.itemId || draft.file?.name || ''}`))
    const nextRequestDrafts = (request.items || [])
      .filter((item) => item?.src)
      .map((item) => ({
        kind: 'request',
        requestId,
        itemId: item.id,
        previewUrl: toApiUrl(item.src),
        label: item.label || '',
        sourceName: requestItemFilename(item),
        src: item.src,
      }))
      .filter((draft) => !existingTemplateSrcs.has(normalizeDraftSrc(draft.src)))
      .filter((draft) => !existingKeys.has(`${draft.kind}:${draft.requestId}:${draft.itemId}`))

    if (nextRequestDrafts.length) {
      uploadItemDrafts.value = [...uploadItemDrafts.value, ...nextRequestDrafts]
    }
  }

  function mergeLibraryItemsIntoDrafts(items, sourceLabel = '') {
    const existingTemplateSrcs = new Set((selectedTemplate.value?.items || []).map((item) => normalizeDraftSrc(item?.src)).filter(Boolean))
    const existingDraftSrcs = new Set(uploadItemDrafts.value.map((draft) => normalizeDraftSrc(draft?.src)).filter(Boolean))
    const existingKeys = new Set(uploadItemDrafts.value.map((draft) => `${draft.kind}:${draft.itemSourceType || ''}:${draft.itemId || draft.file?.name || ''}`))
    const nextDrafts = (items || [])
      .filter((item) => item?.id && item?.src)
      .map((item) => ({
        kind: 'library',
        itemId: item.id,
        itemSourceType: item.sourceType || 'template',
        previewUrl: toApiUrl(item.src),
        label: item.label || '',
        tagsText: Array.isArray(item.tags) ? item.tags.join(', ') : '',
        sourceName: sourceLabel ? `${sourceLabel} · ${item.label || item.id}` : (item.label || item.id),
        src: item.src,
      }))
      .filter((draft) => !existingTemplateSrcs.has(normalizeDraftSrc(draft.src)))
      .filter((draft) => !existingDraftSrcs.has(normalizeDraftSrc(draft.src)))
      .filter((draft) => !existingKeys.has(`${draft.kind}:${draft.itemSourceType}:${draft.itemId}`))

    if (nextDrafts.length) {
      uploadItemDrafts.value = [...uploadItemDrafts.value, ...nextDrafts]
    }
    return nextDrafts.length
  }

  function removeUploadDraft(targetDraft) {
    const targetKey = `${targetDraft.kind}:${targetDraft.requestId || ''}:${targetDraft.itemId || targetDraft.file?.name || ''}:${targetDraft.previewUrl || ''}`
    uploadItemDrafts.value = uploadItemDrafts.value.filter((draft) => {
      const currentKey = `${draft.kind}:${draft.requestId || ''}:${draft.itemId || draft.file?.name || ''}:${draft.previewUrl || ''}`
      return currentKey !== targetKey
    })
    uploadFiles.value = uploadItemDrafts.value.filter((draft) => draft.kind === 'file').map((draft) => draft.file).filter(Boolean)
  }

  async function loadTemplate(options = {}) {
    const preserveUploadState = !!options.preserveUploadState
    resetMessages()
    if (!preserveUploadState) resetUploadState()

    if (!selectedTemplateId.value) {
      selectedTemplate.value = null
      savedTemplateItemOrderIds.value = []
      destroyTemplateItemSortable()
      return
    }

    try {
      isTemplateLoading.value = true
      const data = await api.getTopic(selectedTemplateId.value)
      const loadedTemplate = data.template || data.topic || null
      selectedTemplate.value = {
        ...data,
        template: loadedTemplate,
        items: (data.items || []).map((item) => ({
          ...item,
          draftLabel: item.label,
          draftTags: Array.isArray(item.tags) ? item.tags.join(', ') : '',
        })),
      }
      savedTemplateItemOrderIds.value = (data.items || []).map((item) => item.id)
      await syncTemplateItemSortable()
    } catch (e) {
      selectedTemplate.value = null
      error.value = '템플릿 정보를 불러오지 못했어요.'
    } finally {
      isTemplateLoading.value = false
    }
  }

  async function createTemplate(options = {}) {
    const nextTopicSlug =
      typeof options.topicId === 'string'
        ? options.topicId.trim().toLowerCase()
        : newTemplateId.value.trim().toLowerCase()
    const nextTopicName = typeof options.topicName === 'string' ? options.topicName.trim() : newTemplateName.value.trim()
    const preserveUploadState = !!options.preserveUploadState
    resetMessages()
    try {
      const res = await fetch(toApiUrl('/api/admin/templates'), {
        method: 'POST',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          slug: nextTopicSlug,
          name: nextTopicName,
          isPublic: !!newTemplateIsPublic.value,
          thumbnailSrc: activeTemplateRequest.value?.type === 'create' ? (activeTemplateRequest.value?.thumbnailSrc || '') : '',
        }),
      })
      const data = await res.json()
      if (!res.ok) {
        const requestError = new Error('failed')
        requestError.data = data
        throw requestError
      }
      const createdTemplate = data.template || {}
      if (activeTemplateRequest.value?.type === 'create' && activeTemplateRequest.value?.id) {
        const linkData = await api.linkAdminTemplateRequestTemplate(activeTemplateRequest.value.id, {
          topicId: createdTemplate.id,
        })
        activeTemplateRequest.value = {
          ...activeTemplateRequest.value,
          targetTopicId: linkData.request?.targetTopicId || createdTemplate.id,
          targetTopicName: linkData.request?.targetTopicName || createdTemplate.name || nextTopicName,
        }
        const requestIndex = templateRequests.value.findIndex((entry) => entry.id === activeTemplateRequest.value.id)
        if (requestIndex >= 0) {
          templateRequests.value.splice(requestIndex, 1, {
            ...templateRequests.value[requestIndex],
            targetTopicId: linkData.request?.targetTopicId || createdTemplate.id,
            targetTopicName: linkData.request?.targetTopicName || createdTemplate.name || nextTopicName,
          })
        }
      }
      await refreshTemplates()
      selectedTemplateId.value = createdTemplate.id
      if (customItemModalOpen.value) customItemModalTargetTemplateId.value = createdTemplate.id
      closeTemplateCreateModal()
      await loadTemplate({ preserveUploadState })
      if (!preserveUploadState && activeTemplateRequest.value?.id) {
        const sourceRequest = templateRequests.value.find((entry) => entry.id === activeTemplateRequest.value.id) || activeTemplateRequest.value
        mergeRequestItemsIntoDrafts(sourceRequest)
      }
      success.value = '템플릿이 생성됐어요. 이어서 썸네일과 기본 아이템을 관리할 수 있어요.'
    } catch (e) {
      const errorCode = e?.data?.error || ''
      if (errorCode === 'topic_slug_taken') {
        error.value = '이미 사용 중인 템플릿 주소(slug)입니다.'
        return
      }
      if (errorCode === 'topic_slug_invalid') {
        error.value = '템플릿 주소(slug)는 영문 소문자, 숫자, 하이픈만 사용할 수 있어요.'
        return
      }
      error.value = '템플릿 생성 실패(관리자 권한/템플릿 주소 중복 확인)'
    }
  }

  function handleItemFiles(fileList) {
    const files = Array.from(fileList || []).filter((file) => (file.type || '').startsWith('image/'))
    const requestDrafts = uploadItemDrafts.value.filter((draft) => draft.kind === 'request')
    const previousFileDrafts = uploadItemDrafts.value.filter((draft) => draft.kind === 'file')
    previousFileDrafts.forEach((draft) => {
      if (draft.previewUrl) URL.revokeObjectURL(draft.previewUrl)
    })
    itemPreviewUrls.value = []
    uploadFiles.value = files
    uploadItemDrafts.value = requestDrafts
    if (!files.length) {
      resetFileInput('item')
      return
    }
    itemPreviewUrls.value = files.map((file) => URL.createObjectURL(file))
    const fileDrafts = files.map((file, index) => ({
      kind: 'file',
      file,
      previewUrl: itemPreviewUrls.value[index],
      label: (file.name || '').replace(/\.[^.]+$/, '').replace(/[_-]+/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 60),
      sourceName: file.name,
    }))
    uploadItemDrafts.value = [...requestDrafts, ...fileDrafts]
    resetFileInput('item')
  }

  function onFile(event) {
    handleItemFiles(event.target.files)
  }

  function openItemFilePicker() {
    itemFileInput.value?.click()
  }

  function clearItemFiles() {
    uploadFiles.value = []
    uploadItemDrafts.value = []
    itemPreviewUrls.value.forEach((url) => {
      if (url) URL.revokeObjectURL(url)
    })
    itemPreviewUrls.value = []
    resetFileInput('item')
  }

  async function uploadItem() {
    resetMessages()
    if (!uploadItemDrafts.value.length) {
      error.value = '아이템 파일을 선택해주세요.'
      return
    }

    try {
      if (!selectedTemplateId.value && activeTemplateRequest.value?.type === 'create' && !activeTemplateRequest.value?.targetTopicId) {
        const draftTopicId = (activeTemplateRequest.value?.draftTopicId || '').trim()
        const draftTopicName = (activeTemplateRequest.value?.draftTopicName || '').trim()
        if (!draftTopicId || !draftTopicName) {
          error.value = '먼저 신규 템플릿의 이름과 템플릿 ID를 저장해주세요.'
          return
        }
        await createTemplate({
          topicId: draftTopicId,
          topicName: draftTopicName,
          preserveUploadState: true,
        })
      }

      if (!selectedTemplateId.value) {
        error.value = '템플릿을 먼저 선택해주세요.'
        return
      }

      const fileDrafts = uploadItemDrafts.value.filter((entry) => entry.kind === 'file')
      const requestDrafts = uploadItemDrafts.value.filter((entry) => entry.kind === 'request')
      const libraryDrafts = uploadItemDrafts.value.filter((entry) => entry.kind === 'library')
      const totalUploadBytes = fileDrafts.reduce((sum, entry) => sum + Number(entry.file?.size || 0), 0)
      let uploadCount = 0

      if (fileDrafts.length) {
        console.info('[admin] template item upload start', {
          topicId: selectedTemplateId.value,
          fileCount: fileDrafts.length,
          totalBytes: totalUploadBytes,
          labels: fileDrafts.map((entry) => entry.label.trim()),
        })
        const fd = new FormData()
        fileDrafts.forEach((entry) => {
          fd.append('images', entry.file)
          fd.append('labels', entry.label.trim())
        })
        const res = await fetch(toApiUrl(`/api/admin/templates/${encodeURIComponent(selectedTemplateId.value)}/images`), {
          method: 'POST',
          credentials: 'include',
          body: fd,
        })
        if (!res.ok) {
          const responseText = await res.text().catch(() => '')
          console.error('[admin] template item upload failed', {
            topicId: selectedTemplateId.value,
            fileCount: fileDrafts.length,
            totalBytes: totalUploadBytes,
            status: res.status,
            body: responseText,
          })
          const uploadError = new Error('failed')
          uploadError.status = res.status
          uploadError.body = responseText
          throw uploadError
        }
        console.info('[admin] template item upload success', {
          topicId: selectedTemplateId.value,
          fileCount: fileDrafts.length,
          totalBytes: totalUploadBytes,
        })
        uploadCount += fileDrafts.length
      }

      if (requestDrafts.length) {
        const requestIds = [...new Set(requestDrafts.map((entry) => entry.requestId).filter(Boolean))]
        for (const requestId of requestIds) {
          const draftsForRequest = requestDrafts.filter((entry) => entry.requestId === requestId)
          const result = await api.promoteAdminTemplateRequestItems(requestId, {
            topicId: selectedTemplateId.value,
            itemIds: draftsForRequest.map((entry) => entry.itemId).filter(Boolean),
            itemSrcs: draftsForRequest.map((entry) => entry.src).filter(Boolean),
            itemLabels: draftsForRequest.reduce((acc, entry) => {
              if (entry.itemId) acc[entry.itemId] = entry.label.trim()
              return acc
            }, {}),
          })
          uploadCount += Array.isArray(result?.items) ? result.items.length : 0
        }
      }

      if (libraryDrafts.length) {
        for (const draft of libraryDrafts) {
          const promoted = await api.promoteAdminTemplateItem(draft.itemId, {
            topicId: selectedTemplateId.value,
          })
          const createdItem = promoted?.item || null
          if (createdItem?.id) {
            const nextTags = parseTagsText(draft.tagsText)
            const needsMetaUpdate =
              (draft.label || '').trim() !== (createdItem.label || '').trim() ||
              JSON.stringify(nextTags) !== JSON.stringify(Array.isArray(createdItem.tags) ? createdItem.tags : [])
            if (needsMetaUpdate) {
              await api.updateAdminTemplateItem(selectedTemplateId.value, createdItem.id, {
                label: (draft.label || createdItem.label || '').trim(),
                tags: nextTags,
              })
            }
            uploadCount += 1
          }
        }
      }

      resetUploadState()
      await loadTemplate()
      success.value = `템플릿 기본 아이템 ${uploadCount}개 추가를 완료했어요.`
    } catch (e) {
      console.error('[admin] uploadItem error', {
        message: e?.message || '',
        status: e?.status || 0,
        body: e?.body || '',
        data: e?.data || null,
      })
      const apiError = e?.data?.error || ''
      if (apiError === 'no_items_selected') {
        error.value = '추가할 요청 아이템이 없어요.'
        return
      }
      if (apiError === 'promote_items_failed') {
        const detail = e?.data?.detail ? ` (${e.data.detail})` : ''
        error.value = `요청 아이템을 템플릿 기본 아이템으로 옮기지 못했어요.${detail}`
        return
      }
      if (apiError === 'topic_not_found') {
        error.value = '선택한 템플릿을 찾지 못했어요.'
        return
      }
      if (e?.status === 413) {
        error.value = '한 번에 업로드한 파일 용량이 너무 커서 실패했어요.'
        return
      }
      error.value = '아이템 추가에 실패했어요.'
    }
  }

  async function saveTemplateItemOrder() {
    resetMessages()
    if (!selectedTemplateId.value || !selectedTemplate.value?.items?.length) return

    try {
      const data = await api.updateAdminTemplateItemDisplayOrder(selectedTemplateId.value, {
        itemIds: selectedTemplate.value.items.map((item) => item.id),
      })
      selectedTemplate.value = {
        ...selectedTemplate.value,
        items: (data.items || []).map((item) => ({
          ...item,
          draftLabel: item.label,
          draftTags: Array.isArray(item.tags) ? item.tags.join(', ') : '',
        })),
      }
      savedTemplateItemOrderIds.value = (data.items || []).map((item) => item.id)
      await syncTemplateItemSortable()
      success.value = '기본 아이템 순서를 저장했어요.'
    } catch (e) {
      error.value = '기본 아이템 순서 저장에 실패했어요.'
    }
  }

  return {
    requestItemFilename,
    destroyTemplateItemSortable,
    syncTemplateItemSortable,
    mergeRequestItemsIntoDrafts,
    mergeLibraryItemsIntoDrafts,
    removeUploadDraft,
    loadTemplate,
    createTemplate,
    handleItemFiles,
    onFile,
    openItemFilePicker,
    clearItemFiles,
    uploadItem,
    saveTemplateItemOrder,
  }
}
