import { nextTick } from 'vue' export function useAdminCustomItems({ api, toast, customItems, customItemPage, customItemLimit, customItemPageCount, customItemQuery, customItemFilter, customItemModalOpen, customItemDeleteModalOpen, customItemModalHistoryActive, modalTargetCustomItem, customItemModalDraftLabel, customItemModalLabelSaving, customItemModalTargetTemplateId, customItemReplacementQuery, customItemReplacementItems, customItemReplacementLoading, customItemReplacementTargetId, customItemReplacementBusy, templates, selectedTemplateId, refreshCustomItems, loadTemplate, setTab, selectAdminTemplate, resetMessages, success, error, }) { function submitCustomItemSearch() { customItemPage.value = 1 refreshCustomItems() } function changeCustomItemFilter(filter) { customItemFilter.value = filter customItemPage.value = 1 refreshCustomItems() } function changeCustomItemLimit(limit) { customItemLimit.value = limit customItemPage.value = 1 refreshCustomItems() } function moveCustomItemPage(direction) { const nextPage = customItemPage.value + direction if (nextPage < 1 || nextPage > customItemPageCount.value) return customItemPage.value = nextPage refreshCustomItems() } function pushCustomItemModalHistoryState() { if (typeof window === 'undefined') return window.history.pushState({ ...(window.history.state || {}), adminCustomItemModal: true }, '', window.location.href) customItemModalHistoryActive.value = true } async function refreshReplacementCandidates() { const currentItemId = modalTargetCustomItem.value?.id || '' if (!currentItemId) { customItemReplacementItems.value = [] return } try { customItemReplacementLoading.value = true const data = await api.listAdminCustomItems({ q: customItemReplacementQuery.value, page: 1, limit: 50, filter: 'all', }) customItemReplacementItems.value = (data.items || []).filter((item) => item?.id && item.id !== currentItemId) } catch (e) { error.value = '대체할 이미지 목록을 불러오지 못했어요.' customItemReplacementItems.value = [] } finally { customItemReplacementLoading.value = false } } function openCustomItemModal(item) { modalTargetCustomItem.value = item || null customItemModalDraftLabel.value = item?.label || '' customItemModalTargetTemplateId.value = '' customItemReplacementQuery.value = '' customItemReplacementItems.value = [] customItemReplacementTargetId.value = '' customItemReplacementBusy.value = false customItemModalOpen.value = true pushCustomItemModalHistoryState() } function closeCustomItemModal({ fromPopState = false } = {}) { customItemModalOpen.value = false customItemDeleteModalOpen.value = false modalTargetCustomItem.value = null customItemModalDraftLabel.value = '' customItemModalLabelSaving.value = false customItemModalTargetTemplateId.value = '' customItemReplacementQuery.value = '' customItemReplacementItems.value = [] customItemReplacementTargetId.value = '' customItemReplacementLoading.value = false customItemReplacementBusy.value = false if (fromPopState) { customItemModalHistoryActive.value = false return } if (customItemModalHistoryActive.value && typeof window !== 'undefined') { customItemModalHistoryActive.value = false window.history.back() } } function openCustomItemDeleteModal(item) { if (!item) return if (item.sourceType === 'user' && (item.usageCount > 0 || item.linkedTemplates.length > 0)) { error.value = '사용 중이거나 템플릿에 연결된 사용자 업로드 이미지는 먼저 참조를 정리해야 삭제할 수 있어요.' return } modalTargetCustomItem.value = item customItemDeleteModalOpen.value = true } function closeCustomItemDeleteModal() { customItemDeleteModalOpen.value = false } function jumpToTemplateAdmin(templateId) { if (!templateId) return closeCustomItemModal() setTab('template-admin') nextTick(() => { selectAdminTemplate(templateId) }) } async function removeCustomItem(item = modalTargetCustomItem.value) { resetMessages() if (!item) return if (item.sourceType === 'user' && (item.usageCount > 0 || item.linkedTemplates.length > 0)) { error.value = '사용 중이거나 템플릿에 연결된 사용자 업로드 이미지는 먼저 참조를 정리해야 삭제할 수 있어요.' return } try { await api.deleteAdminCustomItem(item.id) closeCustomItemDeleteModal() closeCustomItemModal() await refreshCustomItems() success.value = item.sourceType === 'template' ? '선택한 템플릿 아이템을 제거했어요.' : item.sourceType === 'asset' ? '선택한 이미지 자산을 삭제했어요.' : '사용자 업로드 이미지를 삭제했어요.' } catch (e) { error.value = item.sourceType === 'template' ? '템플릿 아이템 제거에 실패했어요.' : item.sourceType === 'asset' ? '이미지 자산 삭제에 실패했어요.' : '사용자 업로드 이미지 삭제에 실패했어요.' } } async function removeUnusedCustomItems() { resetMessages() const ok = window.confirm('현재 검색 조건에 맞는 미사용 커스텀 이미지를 모두 삭제할까요?') if (!ok) return try { const data = await api.deleteAdminUnusedCustomItems({ q: customItemQuery.value }) await refreshCustomItems() success.value = `${data.deletedCount || 0}개의 미사용 사용자 업로드 이미지를 삭제했어요.` } catch (e) { error.value = '미사용 커스텀 이미지 일괄 삭제에 실패했어요.' } } async function saveCustomItemModalLabel() { const item = modalTargetCustomItem.value const nextLabel = customItemModalDraftLabel.value.trim().slice(0, 60) if (!item || !nextLabel || nextLabel === item.label || customItemModalLabelSaving.value) return try { customItemModalLabelSaving.value = true const data = await api.updateAdminCustomItemLabel(item.id, { label: nextLabel, sourceType: item.sourceType }) item.label = data.item?.label || nextLabel customItemModalDraftLabel.value = item.label customItems.value = customItems.value.map((entry) => (entry.id === item.id ? { ...entry, label: item.label } : entry)) toast.success('아이템 이름을 변경했어요.') } catch (e) { error.value = '아이템 이름 변경에 실패했어요.' } finally { customItemModalLabelSaving.value = false } } async function promoteCustomItem(item) { resetMessages() if (!customItemModalTargetTemplateId.value) { error.value = '추가할 템플릿을 먼저 선택해주세요.' return } try { item.isPromoting = true await api.promoteAdminTemplateItem(item.id, { topicId: customItemModalTargetTemplateId.value }) const targetTemplateName = templates.value.find((template) => template.id === customItemModalTargetTemplateId.value)?.name || customItemModalTargetTemplateId.value if (selectedTemplateId.value === customItemModalTargetTemplateId.value) await loadTemplate() closeCustomItemModal() success.value = `"${item.label}" 이미지를 ${targetTemplateName} 템플릿으로 추가했어요.` } catch (e) { error.value = '선택한 이미지를 템플릿으로 추가하지 못했어요.' } finally { item.isPromoting = false } } async function replaceCustomItem(item = modalTargetCustomItem.value) { resetMessages() const targetItem = customItemReplacementItems.value.find((entry) => entry.id === customItemReplacementTargetId.value) if (!item?.id) { error.value = '대체할 원본 아이템을 찾지 못했어요.' return } if (!targetItem?.id) { error.value = '대체할 대상 이미지를 먼저 선택해주세요.' return } try { customItemReplacementBusy.value = true const data = await api.replaceAdminCustomItem(item.id, { targetItemId: targetItem.id, targetSourceType: targetItem.sourceType || 'user', }) if (selectedTemplateId.value) await loadTemplate() await refreshCustomItems() closeCustomItemModal() success.value = `"${item.label}" 이미지를 "${data.targetItem?.label || targetItem.label}" 기준으로 대체했어요.` } catch (e) { error.value = e?.status === 409 ? '같은 이미지/이름으로는 대체할 수 없어요.' : '이미지 대체에 실패했어요.' } finally { customItemReplacementBusy.value = false } } return { submitCustomItemSearch, changeCustomItemFilter, changeCustomItemLimit, moveCustomItemPage, pushCustomItemModalHistoryState, openCustomItemModal, closeCustomItemModal, openCustomItemDeleteModal, closeCustomItemDeleteModal, jumpToTemplateAdmin, removeCustomItem, removeUnusedCustomItems, saveCustomItemModalLabel, promoteCustomItem, refreshReplacementCandidates, replaceCustomItem, } }