<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElInput, ElMessage } from 'element-plus'
import { Bell, Document, Operation, Picture, QuestionFilled, Warning } from '@element-plus/icons-vue'
import type { Announcement } from '@/features/announcement'
import type {
  CreateAnnouncementRequest,
  UpdateAnnouncementRequest,
} from '@/features/admin/api/adminAnnouncements'
import { uploadAdminFile } from '@/shared/api/files'
import { debugError } from '@/shared/utils/debug'
import { readError } from '@/shared/utils/error'

interface Props {
  visible: boolean
  mode: 'create' | 'edit'
  announcement?: Announcement | null
}

const props = defineProps<Props>()
const emit = defineEmits<{
  'update:visible': [value: boolean]
  save: [data: CreateAnnouncementRequest | UpdateAnnouncementRequest]
}>()

const formRef = ref<FormInstance>()
const contentInputRef = ref<InstanceType<typeof ElInput>>()
const imageInputRef = ref<HTMLInputElement | null>(null)
const uploadingImage = ref(false)
const imageSettingsVisible = ref(false)
const imageSettingsRange = ref<{ start: number; end: number } | null>(null)
const imageSettingsForm = ref({
  alt: '',
  url: '',
  width: 720,
  useCustomWidth: true,
})
const formData = ref<CreateAnnouncementRequest>({
  title: '',
  content: '',
  category: 'notice',
  priority: 0,
  is_pinned: false,
  is_important: false,
})

const categories = [
  { value: 'notice', label: '通知公告', icon: Bell },
  { value: 'tutorial', label: '使用教程', icon: Document },
  { value: 'rule', label: '规则说明', icon: Warning },
  { value: 'faq', label: '常见问题', icon: QuestionFilled },
]

const rules: FormRules = {
  title: [
    { required: true, message: '请输入公告标题', trigger: 'blur' },
    { max: 255, message: '标题不能超过255个字符', trigger: 'blur' },
  ],
  content: [{ required: true, message: '请输入公告内容', trigger: 'blur' }],
  category: [{ required: true, message: '请选择公告分类', trigger: 'change' }],
}

const dialogTitle = computed(() => {
  return props.mode === 'create' ? '新建公告' : '编辑公告'
})

const imageSettingsPreviewStyle = computed(() => {
  if (!imageSettingsForm.value.useCustomWidth) return {}
  return {
    width: `${imageSettingsForm.value.width}px`,
    maxWidth: '100%',
  }
})

watch(
  () => props.visible,
  val => {
    if (val) {
      if (props.mode === 'edit' && props.announcement) {
        formData.value = {
          title: props.announcement.title,
          content: props.announcement.content,
          category: props.announcement.category as any,
          priority: props.announcement.priority,
          is_pinned: props.announcement.is_pinned,
          is_important: props.announcement.is_important,
        }
      } else {
        formData.value = {
          title: '',
          content: '',
          category: 'notice',
          priority: 0,
          is_pinned: false,
          is_important: false,
        }
      }
      formRef.value?.clearValidate()
    }
  }
)

function handleClose() {
  emit('update:visible', false)
}

async function handleSave() {
  if (!formRef.value) return

  try {
    await formRef.value.validate()
    emit('save', formData.value)
  } catch (error) {
    debugError('表单验证失败', error)
  }
}

function triggerImageUpload() {
  if (uploadingImage.value) return
  imageInputRef.value?.click()
}

async function handleImageChange(event: Event) {
  const input = event.target as HTMLInputElement
  const file = input.files?.[0]
  input.value = ''
  if (!file || uploadingImage.value) return
  if (!['image/jpeg', 'image/png', 'image/webp'].includes(file.type)) {
    ElMessage.warning('仅支持 JPG、PNG 或 WebP 图片')
    return
  }

  uploadingImage.value = true
  try {
    const uploaded = await uploadAdminFile(file, 'announcement')
    insertContentAtCursor(`![${imageAltText(uploaded.filename)}|w=720](${uploaded.medium_url || uploaded.url})`)
    ElMessage.success('图片已插入')
  } catch (error) {
    ElMessage.error(readError(error, '图片上传失败'))
  } finally {
    uploadingImage.value = false
  }
}

function insertContentAtCursor(markdown: string) {
  const textarea = contentInputRef.value?.textarea
  const content = formData.value.content || ''
  if (!textarea) {
    formData.value.content = appendMarkdown(content, markdown)
    return
  }

  const start = textarea.selectionStart ?? content.length
  const end = textarea.selectionEnd ?? content.length
  const before = content.slice(0, start)
  const after = content.slice(end)
  const prefix = before === '' || before.endsWith('\n') ? '' : '\n\n'
  const suffix = after === '' || after.startsWith('\n') ? '' : '\n\n'
  const insertion = `${prefix}${markdown}${suffix}`
  formData.value.content = `${before}${insertion}${after}`

  requestAnimationFrame(() => {
    const cursor = start + insertion.length
    textarea.focus()
    textarea.setSelectionRange(cursor, cursor)
  })
}

function appendMarkdown(content: string, markdown: string) {
  if (!content) return markdown
  return `${content}${content.endsWith('\n') ? '' : '\n\n'}${markdown}`
}

function imageAltText(filename: string) {
  return filename.replace(/\.[^.]+$/, '') || '公告图片'
}

function openImageSettings() {
  const textarea = contentInputRef.value?.textarea
  const cursor = textarea?.selectionStart ?? formData.value.content.length
  const image = findImageMarkdownAtCursor(formData.value.content, cursor)
  if (!image) {
    ElMessage.warning('请先把光标放在一张图片语法中')
    return
  }

  imageSettingsRange.value = { start: image.start, end: image.end }
  imageSettingsForm.value = {
    alt: image.alt,
    url: image.url,
    width: image.width || 720,
    useCustomWidth: Boolean(image.width),
  }
  imageSettingsVisible.value = true
}

function applyImageSettings() {
  const range = imageSettingsRange.value
  if (!range) return
  const alt = imageSettingsForm.value.alt.trim() || '公告图片'
  const url = imageSettingsForm.value.url.trim()
  if (!url) {
    ElMessage.warning('图片地址不能为空')
    return
  }

  const widthMark = imageSettingsForm.value.useCustomWidth ? `|w=${imageSettingsForm.value.width}` : ''
  const nextMarkdown = `![${escapeMarkdownImageText(alt)}${widthMark}](${url})`
  const content = formData.value.content
  formData.value.content = `${content.slice(0, range.start)}${nextMarkdown}${content.slice(range.end)}`
  imageSettingsVisible.value = false

  requestAnimationFrame(() => {
    const textarea = contentInputRef.value?.textarea
    if (!textarea) return
    const cursor = range.start + nextMarkdown.length
    textarea.focus()
    textarea.setSelectionRange(cursor, cursor)
  })
}

function setImageWidth(width: number | null) {
  if (width === null) {
    imageSettingsForm.value.useCustomWidth = false
    return
  }
  imageSettingsForm.value.width = width
  imageSettingsForm.value.useCustomWidth = true
}

function findImageMarkdownAtCursor(content: string, cursor: number) {
  const imagePattern = /!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g
  let match: RegExpExecArray | null
  while ((match = imagePattern.exec(content))) {
    const start = match.index
    const end = start + match[0].length
    if (cursor < start || cursor > end) continue

    const imageText = parseImageText(match[1] || '')
    return {
      start,
      end,
      alt: imageText.alt,
      width: imageText.width,
      url: match[2] || '',
    }
  }
  return null
}

function parseImageText(text: string) {
  const matched = text.match(/^(.*?)(?:\|w=(\d{2,4}))$/)
  if (!matched) return { alt: text, width: null }
  const width = Number(matched[2])
  return {
    alt: matched[1] || '',
    width: Number.isFinite(width) ? width : null,
  }
}

function escapeMarkdownImageText(text: string) {
  return text.replace(/\[/g, '').replace(/\]/g, '')
}
</script>

<template>
  <el-dialog :model-value="visible" :title="dialogTitle" width="800px" @close="handleClose">
    <el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
      <el-form-item label="公告标题" prop="title">
        <el-input
          v-model="formData.title"
          placeholder="请输入公告标题"
          maxlength="255"
          show-word-limit
        />
      </el-form-item>

      <el-form-item label="公告分类" prop="category">
        <el-select v-model="formData.category" placeholder="请选择分类">
          <el-option
            v-for="cat in categories"
            :key="cat.value"
            :label="cat.label"
            :value="cat.value"
          >
            <el-icon style="margin-right: 8px"><component :is="cat.icon" /></el-icon>
            {{ cat.label }}
          </el-option>
        </el-select>
      </el-form-item>

      <el-form-item label="公告内容" prop="content">
        <div class="content-editor">
          <div class="content-toolbar">
            <el-button size="small" :loading="uploadingImage" @click="triggerImageUpload">
              <el-icon><Picture /></el-icon>
              插入图片
            </el-button>
            <el-button size="small" @click="openImageSettings">
              <el-icon><Operation /></el-icon>
              图片设置
            </el-button>
            <input
              ref="imageInputRef"
              class="hidden-file-input"
              type="file"
              accept="image/jpeg,image/png,image/webp"
              @change="handleImageChange"
            />
          </div>
          <el-input
            ref="contentInputRef"
            v-model="formData.content"
            type="textarea"
            :rows="12"
            placeholder="支持 Markdown 格式，可以使用标题、列表、链接和图片"
          />
        </div>
      </el-form-item>

      <el-form-item label="优先级">
        <el-input-number
          v-model="formData.priority"
          :min="0"
          :max="999"
          placeholder="数值越大越靠前"
        />
        <span style="margin-left: 12px; color: #909399; font-size: 13px"
          >数值越大越靠前，默认为0</span
        >
      </el-form-item>

      <el-form-item label="标记">
        <el-checkbox v-model="formData.is_pinned">置顶</el-checkbox>
        <el-checkbox v-model="formData.is_important" style="margin-left: 20px">重要</el-checkbox>
      </el-form-item>
    </el-form>

    <template #footer>
      <el-button @click="handleClose">取消</el-button>
      <el-button type="primary" @click="handleSave">保存</el-button>
    </template>
  </el-dialog>

  <el-dialog v-model="imageSettingsVisible" title="图片设置" width="520px">
    <div class="image-settings-preview">
      <img
        v-if="imageSettingsForm.url"
        :src="imageSettingsForm.url"
        :alt="imageSettingsForm.alt"
        :style="imageSettingsPreviewStyle"
      />
    </div>
    <el-form label-width="84px">
      <el-form-item label="图片说明">
        <el-input v-model="imageSettingsForm.alt" placeholder="用于图片替代文字" />
      </el-form-item>
      <el-form-item label="显示宽度">
        <div class="image-width-control">
          <el-switch v-model="imageSettingsForm.useCustomWidth" active-text="自定义" inactive-text="原宽" />
          <el-slider
            v-model="imageSettingsForm.width"
            :min="120"
            :max="1200"
            :step="10"
            :disabled="!imageSettingsForm.useCustomWidth"
          />
          <el-input-number
            v-model="imageSettingsForm.width"
            :min="120"
            :max="1200"
            :step="10"
            :disabled="!imageSettingsForm.useCustomWidth"
            controls-position="right"
          />
        </div>
      </el-form-item>
      <el-form-item label="快捷尺寸">
        <div class="quick-widths">
          <el-button size="small" @click="setImageWidth(320)">小</el-button>
          <el-button size="small" @click="setImageWidth(720)">中</el-button>
          <el-button size="small" @click="setImageWidth(960)">大</el-button>
          <el-button size="small" @click="setImageWidth(null)">原宽</el-button>
        </div>
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="imageSettingsVisible = false">取消</el-button>
      <el-button type="primary" @click="applyImageSettings">应用</el-button>
    </template>
  </el-dialog>
</template>

<style scoped>
.content-editor {
  width: 100%;
}

.content-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.hidden-file-input {
  display: none;
}

.image-settings-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 180px;
  margin-bottom: 20px;
  padding: 16px;
  border: 1px solid #e6eaf2;
  border-radius: 8px;
  background: #f8fafc;
}

.image-settings-preview img {
  display: block;
  max-width: 100%;
  max-height: 260px;
  height: auto;
  border-radius: 6px;
}

.image-width-control {
  display: grid;
  grid-template-columns: auto minmax(160px, 1fr) 120px;
  align-items: center;
  width: 100%;
  gap: 12px;
}

.quick-widths {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

:deep(.el-textarea__inner) {
  font-family: 'SF Mono', 'Menlo', 'Consolas', monospace;
  font-size: 13px;
  line-height: 1.6;
}
</style>
