<script setup lang="ts">
import { EyeOutlined, LeftOutlined, SaveOutlined, SendOutlined } from '@ant-design/icons-vue'
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
import { message } from 'ant-design-vue'
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'

import EditorAiAssistPanel from '@/components/editor-common/EditorAiAssistPanel.vue'
import { kolManageApi } from '@/lib/api'
import { formatError } from '@/lib/errors'
import { mergeKolCardConfig, parseKolCardConfig } from '@/lib/kol-card-config'
import {
  getKolVariableInitialValue,
  normalizeKolVariableDefinition,
  syncKolVariablesWithContent,
} from '@/lib/kol-placeholders'
import { buildKolPlatformOptions } from '@/lib/kol-platform-options'
import type {
  KolAssistRequest,
  KolAssistStreamEvent,
  KolCardConfig,
  KolVariableDefinition,
} from '@geo/shared-types'
import KolDynamicForm from './KolDynamicForm.vue'
import KolPromptEditArea from './KolPromptEditArea.vue'
import KolVariableConfig from './KolVariableConfig.vue'
import KolVariablePanel from './KolVariablePanel.vue'

const props = defineProps<{
  promptId: number
}>()

type AiOptimizeStatus = 'idle' | 'generating' | 'completed' | 'error'
type EditorAiAssistPlacement = 'bottom' | 'top'
type EditorAiAssistBoundary = {
  left: number
  top: number
  right: number
  bottom: number
}

type PromptSelectionAiSnapshot = {
  start: number
  end: number
  selectedText: string
  x: number
  y: number
  width: number
  placement: EditorAiAssistPlacement
  boundary: EditorAiAssistBoundary
}

type PromptSelectionAiPanelState = PromptSelectionAiSnapshot & {
  open: boolean
}

const emit = defineEmits<{
  back: []
}>()

const { t } = useI18n()
const queryClient = useQueryClient()

function createDefaultPromptSelectionAiPanelState(): PromptSelectionAiPanelState {
  return {
    open: false,
    x: 0,
    y: 0,
    width: 0,
    placement: 'bottom',
    boundary: { left: 0, top: 0, right: 0, bottom: 0 },
    start: 0,
    end: 0,
    selectedText: '',
  }
}

const content = ref('')
const variables = ref<KolVariableDefinition[]>([])
const previewOpen = ref(false)
const previewValues = ref<Record<string, any>>({})
const aiBusy = ref(false)
const selectionAiPanel = ref<PromptSelectionAiPanelState>(
  createDefaultPromptSelectionAiPanelState(),
)
const selectionAiInstruction = ref('')
const selectionAiPreview = ref('')
const selectionAiError = ref('')
const selectionAiStatus = ref<AiOptimizeStatus>('idle')
const cardConfig = ref<KolCardConfig>({})
const variableConfigRef = ref<{
  focusVariable: (key: string, options?: { focusInput?: boolean }) => void
} | null>(null)
const metadataForm = reactive({
  name: '',
  platform_hint: '通用',
  allow_web_search: false,
  allow_user_knowledge: true,
})

const { data: promptDetail } = useQuery({
  queryKey: computed(() => ['kol', 'prompt', props.promptId]),
  queryFn: () => kolManageApi.getPrompt(props.promptId),
})

const platformOptions = computed(() => {
  const options = buildKolPlatformOptions(t('kol.manage.platformHintDefault'))
  const currentValue = metadataForm.platform_hint.trim()
  if (currentValue && !options.some((option) => option.value === currentValue)) {
    return [{ label: currentValue, value: currentValue }, ...options]
  }
  return options
})
const selectionAiStreaming = computed(() => selectionAiStatus.value === 'generating')
const selectionAiHasPreview = computed(() => selectionAiPreview.value.trim().length > 0)
const selectionAiUiText = computed(() => ({
  title: t('article.editor.aiOptimize.title'),
  promptPlaceholder: t('kol.manage.editor.aiOptimizePromptPlaceholder'),
  generating: t('article.editor.aiOptimize.generating'),
  failed: t('article.editor.aiOptimize.messages.failed'),
  disclaimer: t('kol.manage.editor.aiOptimizeDisclaimer'),
  regenerate: t('article.editor.aiOptimize.regenerate'),
  close: t('article.editor.aiOptimize.close'),
  replace: t('kol.manage.editor.aiOptimizeReplace'),
}))

watch(
  () => promptDetail.value,
  (detail) => {
    if (detail) {
      const nextCardConfig = detail.latest_card_config_json ?? {}
      const parsedCardConfig = parseKolCardConfig(nextCardConfig)

      metadataForm.name = detail.name
      metadataForm.platform_hint = detail.platform_hint || '通用'
      metadataForm.allow_web_search = parsedCardConfig.allow_web_search
      metadataForm.allow_user_knowledge = parsedCardConfig.allow_user_knowledge
      content.value = detail.latest_prompt_content ?? ''
      variables.value = (detail.latest_schema_json?.variables ?? []).map((variable) =>
        normalizeKolVariableDefinition(variable),
      )
      cardConfig.value = { ...nextCardConfig }
    }
  },
  { immediate: true },
)

const publishMutation = useMutation({
  mutationFn: (payload: ReturnType<typeof buildPromptPayload>) =>
    kolManageApi.publishPrompt(props.promptId, payload),
  onSuccess: () => {
    message.success(t('kol.manage.editor.publish') + '成功')
    invalidatePromptQueries()
  },
  onError: (error) => {
    message.error(formatError(error))
  },
})

const saveMutation = useMutation({
  mutationFn: (payload: ReturnType<typeof buildPromptPayload>) =>
    kolManageApi.savePrompt(props.promptId, payload),
  onSuccess: () => {
    message.success(t('common.save') + '成功')
    invalidatePromptQueries()
  },
  onError: (error) => {
    message.error(formatError(error))
  },
})

let aiAssistAbortController: AbortController | null = null
let selectionAiAbortController: AbortController | null = null

function applyAssistResult(event: KolAssistStreamEvent) {
  const nextContent = (event.content ?? '').trim()
  if (!nextContent) {
    return
  }

  content.value = nextContent
  variables.value = syncKolVariablesWithContent(
    nextContent,
    event.schema?.variables ?? variables.value,
  )
}

function handleAssistStreamEvent(event: KolAssistStreamEvent): 'completed' | 'error' | null {
  if (event.type === 'start') {
    aiBusy.value = true
    return null
  }

  if (event.type === 'delta') {
    aiBusy.value = true
    if (typeof event.content === 'string') {
      content.value = event.content
    } else if (typeof event.delta === 'string' && event.delta) {
      content.value += event.delta
    }
    return null
  }

  if (event.type === 'completed') {
    aiBusy.value = false
    applyAssistResult(event)
    message.success('AI 处理完成')
    return 'completed'
  }

  if (event.type === 'error') {
    aiBusy.value = false
    if (typeof event.content === 'string' && event.content.trim()) {
      content.value = event.content
    }
    message.error(event.error || 'AI 处理失败')
    return 'error'
  }

  return null
}

function stopAiAssistStream() {
  aiAssistAbortController?.abort()
  aiAssistAbortController = null
  aiBusy.value = false
}

async function runAiAssistStream(payload: KolAssistRequest) {
  stopAiAssistStream()

  const controller = new AbortController()
  aiAssistAbortController = controller
  aiBusy.value = true
  let terminalState: 'completed' | 'error' | null = null

  try {
    await kolManageApi.streamAssist(
      payload,
      {
        onEvent(event) {
          terminalState = handleAssistStreamEvent(event)
        },
        onClose() {
          if (aiAssistAbortController === controller && terminalState === null) {
            aiBusy.value = false
          }
        },
      },
      controller.signal,
    )

    if (!controller.signal.aborted && terminalState === null) {
      aiBusy.value = false
      message.error('AI 处理失败')
    }
  } catch (error) {
    if (controller.signal.aborted) {
      return
    }

    aiBusy.value = false
    message.error(formatError(error))
  } finally {
    if (aiAssistAbortController === controller) {
      aiAssistAbortController = null
    }
  }
}

async function handleAiGenerate(description: string) {
  await runAiAssistStream({
    mode: 'generate',
    description,
    prompt_id: props.promptId,
  })
}

function resetSelectionAiOutput() {
  selectionAiPreview.value = ''
  selectionAiError.value = ''
  selectionAiStatus.value = 'idle'
}

function stopSelectionAiStream() {
  selectionAiAbortController?.abort()
  selectionAiAbortController = null

  if (selectionAiStatus.value === 'generating') {
    selectionAiStatus.value = selectionAiHasPreview.value ? 'completed' : 'idle'
    selectionAiError.value = ''
  }
}

function closeSelectionAiPanel() {
  stopSelectionAiStream()
  selectionAiPanel.value = createDefaultPromptSelectionAiPanelState()
  selectionAiInstruction.value = ''
  resetSelectionAiOutput()
}

function handleOpenSelectionAiPanel(snapshot: PromptSelectionAiSnapshot) {
  stopAiAssistStream()
  stopSelectionAiStream()
  selectionAiPanel.value = {
    open: true,
    ...snapshot,
  }
  selectionAiInstruction.value = ''
  resetSelectionAiOutput()
}

function handleSelectionAiStreamEvent(event: KolAssistStreamEvent) {
  if (event.type === 'start') {
    selectionAiStatus.value = 'generating'
    selectionAiError.value = ''
    return
  }

  if (event.type === 'delta') {
    selectionAiStatus.value = 'generating'
    selectionAiError.value = ''
    if (typeof event.content === 'string') {
      selectionAiPreview.value = event.content
    } else if (typeof event.delta === 'string') {
      selectionAiPreview.value += event.delta
    }
    return
  }

  if (event.type === 'completed') {
    selectionAiStatus.value = 'completed'
    selectionAiError.value = ''
    if (typeof event.content === 'string') {
      selectionAiPreview.value = event.content
    }
    return
  }

  if (event.type === 'error') {
    selectionAiStatus.value = selectionAiHasPreview.value ? 'completed' : 'error'
    selectionAiError.value = event.error || t('article.editor.aiOptimize.messages.failed')
    if (typeof event.content === 'string' && event.content.trim()) {
      selectionAiPreview.value = event.content
    }
    if (!selectionAiHasPreview.value) {
      message.error(selectionAiError.value)
    }
  }
}

async function generateSelectionAiPreview() {
  if (!selectionAiPanel.value.open) {
    return
  }

  const instruction = selectionAiInstruction.value.trim()
  if (!instruction) {
    message.warning(t('article.editor.aiOptimize.messages.promptRequired'))
    return
  }

  stopSelectionAiStream()
  selectionAiPreview.value = ''
  selectionAiError.value = ''
  selectionAiStatus.value = 'generating'

  const syncedVariables = syncVariablesFromContent()
  const controller = new AbortController()
  selectionAiAbortController = controller

  try {
    await kolManageApi.streamAssist(
      {
        mode: 'optimize',
        description: instruction,
        current_content: selectionAiPanel.value.selectedText,
        schema: { variables: syncedVariables },
        prompt_id: props.promptId,
      },
      {
        onEvent: handleSelectionAiStreamEvent,
      },
      controller.signal,
    )

    if (!controller.signal.aborted && selectionAiStatus.value === 'generating') {
      selectionAiStatus.value = selectionAiHasPreview.value ? 'completed' : 'error'
    }
  } catch (error) {
    if (controller.signal.aborted) {
      return
    }

    selectionAiStatus.value = selectionAiHasPreview.value ? 'completed' : 'error'
    selectionAiError.value = formatError(error)
    if (!selectionAiHasPreview.value) {
      message.error(selectionAiError.value)
    }
  } finally {
    if (selectionAiAbortController === controller) {
      selectionAiAbortController = null
    }
  }
}

function applySelectionAiReplacement() {
  const replacement = selectionAiPreview.value.trim()
  if (!selectionAiPanel.value.open || !replacement) {
    if (!replacement) {
      message.warning(t('article.editor.aiOptimize.messages.empty'))
    }
    return
  }

  const target = { ...selectionAiPanel.value }
  const currentSelectedText = content.value.slice(target.start, target.end)
  if (currentSelectedText !== target.selectedText) {
    message.warning(t('article.editor.aiOptimize.messages.selectionChanged'))
    return
  }

  const nextContent =
    content.value.slice(0, target.start) + replacement + content.value.slice(target.end)
  content.value = nextContent
  variables.value = syncKolVariablesWithContent(nextContent, variables.value)
  closeSelectionAiPanel()
}

function buildPromptPayload(nextVariables: KolVariableDefinition[] = variables.value) {
  return {
    name: metadataForm.name.trim(),
    platform_hint: metadataForm.platform_hint,
    sort_order: promptDetail.value?.sort_order,
    content: content.value,
    schema: { variables: nextVariables },
    card_config: mergeKolCardConfig(cardConfig.value, {
      allow_web_search: metadataForm.allow_web_search,
      allow_user_knowledge: metadataForm.allow_user_knowledge,
    }),
  }
}

function syncVariablesFromContent(): KolVariableDefinition[] {
  const syncedVariables = syncKolVariablesWithContent(content.value, variables.value)
  variables.value = syncedVariables
  return syncedVariables
}

function buildPreviewValues(nextVariables: KolVariableDefinition[]): Record<string, any> {
  const nextValues: Record<string, any> = {}

  nextVariables.forEach((variable) => {
    const fieldKey = variable.key?.trim() || variable.id
    nextValues[fieldKey] = getKolVariableInitialValue(variable)
  })

  return nextValues
}

function invalidatePromptQueries() {
  void queryClient.invalidateQueries({ queryKey: ['kol', 'prompt', props.promptId] })
  void queryClient.invalidateQueries({ queryKey: ['kol', 'packages'] })
  void queryClient.invalidateQueries({
    queryKey: ['kol', 'packages', promptDetail.value?.package_id, 'prompts'],
  })
}

function validateBeforeSubmit(): boolean {
  if (!metadataForm.name.trim()) {
    message.warning(t('common.inputPlease') + t('common.title'))
    return false
  }
  if (!content.value.trim()) {
    message.warning('请输入提示词内容')
    return false
  }
  return true
}

function handleSave() {
  const syncedVariables = syncVariablesFromContent()
  if (!validateBeforeSubmit()) {
    return
  }
  saveMutation.mutate(buildPromptPayload(syncedVariables))
}

function handleSaveShortcut(event: KeyboardEvent) {
  if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 's') {
    return
  }

  event.preventDefault()
  event.stopPropagation()
  if (event.repeat || saveMutation.isPending.value || publishMutation.isPending.value) {
    return
  }

  handleSave()
}

function handlePublish() {
  const syncedVariables = syncVariablesFromContent()
  if (!validateBeforeSubmit()) {
    return
  }
  publishMutation.mutate(buildPromptPayload(syncedVariables))
}

function handlePreview() {
  const syncedVariables = syncVariablesFromContent()
  previewValues.value = buildPreviewValues(syncedVariables)
  previewOpen.value = true
}

async function handleFocusVariable(key: string) {
  syncVariablesFromContent()
  await nextTick()
  variableConfigRef.value?.focusVariable(key, { focusInput: false })
}

function getSelectPopupContainer(triggerNode: HTMLElement) {
  return triggerNode.parentElement ?? triggerNode
}

function statusTagColor(status?: string): string {
  if (status === 'active') return 'green'
  if (status === 'archived') return 'orange'
  return 'default'
}

function statusLabel(status?: string): string {
  if (status === 'active') return t('kol.manage.status.active')
  if (status === 'archived') return t('kol.manage.status.archived')
  return t('kol.manage.status.draft')
}

function handleWindowPointerDown(event: PointerEvent) {
  if (!(selectionAiPanel.value.open && selectionAiStatus.value === 'idle')) {
    return
  }

  const target = event.target
  if (
    target instanceof Element &&
    (target.closest('.editor-ai-assist') || target.closest('.prompt-selection-ai-action'))
  ) {
    return
  }

  closeSelectionAiPanel()
}

onMounted(() => {
  window.addEventListener('keydown', handleSaveShortcut)
  window.addEventListener('pointerdown', handleWindowPointerDown)
})

onBeforeUnmount(() => {
  window.removeEventListener('keydown', handleSaveShortcut)
  window.removeEventListener('pointerdown', handleWindowPointerDown)
  stopAiAssistStream()
  stopSelectionAiStream()
})
</script>

<template>
  <div class="kol-prompt-editor">
    <div class="editor-topbar">
      <button class="back-btn" type="button" @click="emit('back')">
        <LeftOutlined />
        <span>{{ t('common.back') }}</span>
      </button>
    </div>

    <div class="editor-header">
      <div class="header-title">
        <div class="title-row">
          <span class="prompt-name">
            {{ metadataForm.name || promptDetail?.name || t('common.loading') }}
          </span>
          <a-tag v-if="promptDetail" :color="statusTagColor(promptDetail.status)">
            {{ statusLabel(promptDetail.status) }}
          </a-tag>
        </div>
        <span class="prompt-subtitle">{{ t('kol.manage.editor.basicInfo') }}</span>
      </div>
      <div class="header-actions">
        <a-button @click="handlePreview">
          <template #icon><EyeOutlined /></template>
          {{ t('kol.manage.editor.preview') }}
        </a-button>
        <a-button
          :loading="saveMutation.isPending.value"
          title="Ctrl/Cmd + S"
          aria-keyshortcuts="Control+S Meta+S"
          @click="handleSave"
        >
          <template #icon><SaveOutlined /></template>
          {{ t('common.save') }}
        </a-button>
        <a-button type="primary" :loading="publishMutation.isPending.value" @click="handlePublish">
          <template #icon><SendOutlined /></template>
          {{ t('kol.manage.editor.publish') }}
        </a-button>
      </div>
    </div>

    <div class="editor-meta-card">
      <div class="editor-section-title">{{ t('kol.manage.editor.basicInfo') }}</div>
      <a-form layout="vertical" class="editor-meta-form">
        <a-form-item :label="t('common.title')" required>
          <a-input
            v-model:value="metadataForm.name"
            :placeholder="t('common.inputPlease') + t('common.title')"
          />
        </a-form-item>

        <a-form-item :label="t('kol.manage.platformHint')">
          <a-select
            v-model:value="metadataForm.platform_hint"
            :options="platformOptions"
            option-filter-prop="label"
            show-search
            :get-popup-container="getSelectPopupContainer"
          />
        </a-form-item>

        <a-form-item label="生成文章时允许联网搜索">
          <div class="editor-switch-field">
            <a-switch v-model:checked="metadataForm.allow_web_search" />
            <span class="editor-switch-hint">默认关闭，关闭后用户在生成页无法启用联网搜索。</span>
          </div>
        </a-form-item>

        <a-form-item label="生成文章时允许用户使用自己的知识库">
          <div class="editor-switch-field">
            <a-switch v-model:checked="metadataForm.allow_user_knowledge" />
            <span class="editor-switch-hint">
              默认开启，关闭后用户在生成页无法选择自己的知识库。
            </span>
          </div>
        </a-form-item>
      </a-form>
    </div>

    <div class="editor-body">
      <KolVariablePanel />
      <KolPromptEditArea
        v-model:content="content"
        v-model:variables="variables"
        :ai-busy="aiBusy"
        :selection-ai-open="selectionAiPanel.open"
        @ai-generate="handleAiGenerate"
        @ai-optimize-selection="handleOpenSelectionAiPanel"
        @focus-variable="handleFocusVariable"
      />
      <KolVariableConfig ref="variableConfigRef" v-model:variables="variables" />
    </div>

    <a-modal
      v-model:open="previewOpen"
      :title="t('kol.manage.editor.preview')"
      :footer="null"
      width="600px"
    >
      <KolDynamicForm v-model:modelValue="previewValues" :variables="variables" />
    </a-modal>

    <EditorAiAssistPanel
      v-if="selectionAiPanel.open"
      v-model:prompt="selectionAiInstruction"
      :x="selectionAiPanel.x"
      :y="selectionAiPanel.y"
      :width="selectionAiPanel.width"
      :placement="selectionAiPanel.placement"
      :boundary="selectionAiPanel.boundary"
      :status="selectionAiStatus"
      :streaming="selectionAiStreaming"
      :has-preview="selectionAiHasPreview"
      :error="selectionAiError"
      :title="selectionAiUiText.title"
      :prompt-placeholder="selectionAiUiText.promptPlaceholder"
      :continue-placeholder="selectionAiUiText.promptPlaceholder"
      :loading-label="selectionAiUiText.generating"
      :error-label="selectionAiUiText.failed"
      :disclaimer="selectionAiUiText.disclaimer"
      :regenerate-label="selectionAiUiText.regenerate"
      :discard-label="selectionAiUiText.close"
      :apply-label="selectionAiUiText.replace"
      @submit="generateSelectionAiPreview"
      @close="closeSelectionAiPanel"
      @reset="resetSelectionAiOutput"
      @apply="applySelectionAiReplacement"
    >
      <template #preview>
        <pre class="kol-prompt-editor__ai-preview">{{ selectionAiPreview }}</pre>
      </template>
    </EditorAiAssistPanel>
  </div>
</template>

<style scoped>
.kol-prompt-editor {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.editor-topbar {
  padding: 12px 16px;
}

.back-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 0;
  border: 0;
  background: transparent;
  color: #111827;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}

.back-btn :deep(.anticon) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.82);
  border: 1px solid rgba(144, 157, 181, 0.24);
}

.editor-header {
  margin: 0 16px;
  padding: 16px 20px;
  border: 1px solid #f0f0f0;
  border-radius: 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  background: #fff;
}

.header-title {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.title-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.prompt-name {
  font-size: 18px;
  font-weight: 600;
  color: #111827;
}

.prompt-subtitle {
  font-size: 13px;
  color: #667085;
}

.header-actions {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

.editor-meta-card {
  margin: 12px 16px 0;
  padding: 16px 20px 4px;
  border: 1px solid #f0f0f0;
  border-radius: 16px;
  background: #fff;
}

.editor-section-title {
  margin: 0 0 16px;
  font-size: 14px;
  font-weight: 600;
  color: #111827;
}

.editor-meta-form {
  display: grid;
  grid-template-columns: minmax(240px, 1.4fr) minmax(200px, 1fr);
  gap: 16px;
  align-items: start;
}

.editor-body {
  flex: 1;
  display: flex;
  overflow: hidden;
  margin: 12px 16px 16px;
  border: 1px solid #f0f0f0;
  border-radius: 16px;
  background: #f0f0f0;
}

.editor-switch-field {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 32px;
}

.editor-switch-hint {
  color: #667085;
  font-size: 13px;
  line-height: 1.5;
}

.kol-prompt-editor__ai-preview {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  color: #111827;
  font: inherit;
  line-height: 1.8;
}

@media (max-width: 1200px) {
  .editor-meta-form {
    grid-template-columns: 1fr 1fr;
  }
}

@media (max-width: 900px) {
  .editor-header {
    flex-direction: column;
    align-items: flex-start;
  }

  .header-actions {
    width: 100%;
    justify-content: flex-start;
  }

  .editor-meta-form {
    grid-template-columns: 1fr;
  }

  .editor-body {
    flex-direction: column;
  }
}
</style>
