<template>
  <div class="x-markdown-viewer-wrapper">
    <div
      v-if="showControlButton"
      class="stream-control-bar"
    >
      <a-button
        class="stream-btn"
        :class="buttonClass"
        @click="handleButtonClick"
      >
        <a-icon :type="getButtonIcon()" />
        <span class="btn-text">{{ getButtonText() }}</span>
      </a-button>
    </div>
    <div ref="containerRef" class="x-markdown-viewer">
      <template v-for="(seg, idx) in segments">
        <div
          v-if="seg.type === 'md'"
          :key="`md-${idx}`"
          class="markdown-body"
          v-html="renderMd(seg.content)"
        />
        <XList
          v-else-if="seg.type === 'component' && seg.componentType === 'x-list'"
          :key="`xlist-${idx}`"
          :queryParamsName="seg.configName"
          :initialData="seg.data"
        />
      </template>
    </div>
  </div>
</template>

<script>
import { marked } from 'marked'
import { parseMarkdownWithJsonBlocks } from './parseMarkdownWithJsonBlocks'
import XList from '@vue2-client/base-client/components/his/XList/XList.vue'
import { startEventStreamPOST } from '@vue2-client/services/api/restTools'
import { getConfigByName } from '@vue2-client/services/api/common'
import moment from 'moment/moment'

/**
 * HTML 转义，防止 XSS
 */
function escapeHtml (s) {
  if (s == null) return ''
  return String(s)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
}

/**
 * 标准化 markdown 输入，支持多种格式
 */
function normalizeMdInput (v, onWarn) {
  if (typeof v === 'string') return v === '[object Object]' ? '' : v
  if (v && typeof v === 'object') {
    if (typeof v.default === 'string') return v.default
    if (typeof v.content === 'string') return v.content
    if (typeof onWarn === 'function') onWarn(v)
    return ''
  }
  const s = v == null ? '' : String(v)
  return s === '[object Object]' ? '' : s
}

/**
 * 判断是否为 Blob/File 对象
 */
function isBlobLike (v) {
  if (!v || typeof v !== 'object') return false
  return typeof v.size === 'number' && typeof v.type === 'string' && (typeof v.text === 'function' || typeof FileReader !== 'undefined')
}

/**
 * 将 Blob 读取为文本
 */
function readBlobAsText (blob) {
  if (blob && typeof blob.text === 'function') return blob.text()
  return new Promise((resolve, reject) => {
    try {
      const reader = new FileReader()
      reader.onload = () => resolve(reader.result == null ? '' : String(reader.result))
      reader.onerror = () => reject(reader.error || new Error('read file error'))
      reader.readAsText(blob)
    } catch (e) {
      reject(e)
    }
  })
}

export default {
  name: 'XMarkdownViewer',
  components: { XList },
  props: {
    /**
     * Markdown 文本（推荐使用 markdown；value 用于兼容 v-model/旧写法）
     */
    markdown: {
      type: [String, Object],
      default: ''
    },
    value: {
      type: [String, Object],
      default: ''
    },
    /**
     * 是否允许 Markdown 中的原始 HTML 被解析渲染
     */
    allowHtml: {
      type: Boolean,
      default: false
    },
    /**
     * marked 解析参数覆盖
     */
    markedOptions: {
      type: Object,
      default: () => ({})
    },
    /**
     * 纯数组 JSON 代码块时默认的 x-list 配置名
     */
    defaultXListConfigName: {
      type: String,
      default: ''
    },
    /**
     * 是否显示控制按钮
     */
    showControlButton: {
      type: Boolean,
      default: true
    },
    /**
     * 按钮配置名称（用于从配置中心获取按钮文字等配置）
     */
    queryParamsName: {
      type: String,
      default: ''
    },
    /**
     * 服务名
     */
    serviceName: {
      type: String,
      default: process.env.VUE_APP_SYSTEM_NAME
    }
  },
  emits: {
    'start-stream': null,
    'stop-stream': null,
    'configEnd': null
  },
  data () {
    return {
      /**
       * 通过实例方法 receiveMarkdown() 写入的 md 内容
       */
      internalMarkdown: null,
      /**
       * 容器 DOM 引用
       */
      containerRef: null,
      /**
       * 是否正在流式输出
       */
      isStreaming: false,
      /**
       * 流是否已结束
       */
      streamEnded: false,
      /**
       * 按钮配置（从配置中心加载，无默认值）
       */
      buttonConfig: {},
      /**
       * 环境变量
       */
      env: process.env.NODE_ENV || 'production',
      /**
       * 是否始终显示按钮
       */
      showButtonInitially: true,
      /**
       * 配置是否已加载完成
       */
      // eslint-disable-next-line vue/no-reserved-keys
      configEndEmitted: false,
      /**
       * 配置结束 Promise
       */
      // eslint-disable-next-line vue/no-reserved-keys
      configEndPromise: null,
      /**
       * 配置结束 resolve 函数
       */
      // eslint-disable-next-line vue/no-reserved-keys
      configEndResolve: null
    }
  },
  /**
   * 监听按钮配置名称变化，自动加载配置
   */
  watch: {
    queryParamsName: {
      immediate: true,
      handler (newVal) {
        this.loadButtonConfig(newVal)
      },
      deep: true
    }
  },
  computed: {
    buttonClass() {
      return {
        'is-streaming': this.isStreaming
      }
    },
    normalizedMd () {
      if (this.internalMarkdown !== null && this.internalMarkdown !== undefined) {
        return normalizeMdInput(this.internalMarkdown, v => {
          console.warn('[XMarkdownViewer] internalMarkdown 期望是字符串，但收到对象：', v)
        })
      }
      const v = (this.markdown != null && this.markdown !== '') ? this.markdown : (this.value || '')
      return normalizeMdInput(v, vv => {
        console.warn('[XMarkdownViewer] markdown/value 期望是字符串，但收到对象：', vv)
      })
    },
    segments () {
      return parseMarkdownWithJsonBlocks(this.normalizedMd, {
        defaultXListConfigName: this.defaultXListConfigName || undefined
      })
    }
  },
  methods: {
    /**
     * 初始化按钮配置
     */
    initButtonConfig(parameter) {
      this.configEndEmitted = false
      this.configEndPromise = new Promise(resolve => { this.configEndResolve = resolve })
      if (!parameter) {
        this._emitConfigEnd()
        return
      }
      this.loadButtonConfig(parameter)
    },
    /**
     * 配置加载结束后发出事件
     */
    _emitConfigEnd () {
      if (this.configEndEmitted) return
      this.configEndEmitted = true
      if (this.configEndResolve) {
        this.configEndResolve()
        this.configEndResolve = null
      }
      this.$emit('configEnd')
    },
    /**
     * 等待配置结束的异步方法
     */
    waitConfigEnd () {
      if (this.configEndEmitted) return Promise.resolve()
      return this.configEndPromise || Promise.resolve()
    },
    /**
     * 通过组件实例接收 md 文件
     */
    async receiveMarkdown (file) {
      if (!isBlobLike(file)) {
        console.warn('[XMarkdownViewer] receiveMarkdown 仅支持 File/Blob，但收到：', file)
        this.internalMarkdown = ''
        return ''
      }
      const text = await readBlobAsText(file)
      this.internalMarkdown = text
      return text
    },
    /**
     * 接收字符串格式的 markdown 内容
     */
    receiveMarkdownString (markdownText) {
      if (typeof markdownText !== 'string') {
        console.warn('[XMarkdownViewer] receiveMarkdownString 期望接收字符串，但收到：', markdownText)
        this.internalMarkdown = ''
        return ''
      }
      this.internalMarkdown = markdownText
      return markdownText
    },
    /**
     * receiveMarkdown 的别名
     */
    async receiveFile (file) {
      return await this.receiveMarkdown(file)
    },
    /**
     * 清除通过 receiveMarkdown() 写入的内容
     */
    clearReceivedMarkdown () {
      this.internalMarkdown = null
    },
    /**
     * 渲染 Markdown
     */
    renderMd (text) {
      const s = this.allowHtml ? (text || '') : escapeHtml(text || '')
      try {
        return marked.parse(s, {
          breaks: true,
          gfm: true,
          headerIds: false,
          mangle: false,
          ...this.markedOptions
        })
      } catch (e) {
        console.error('[XMarkdownViewer] Markdown 渲染错误:', e)
        return s
      }
    },
    /**
     * 调用 AI 流式接口
     */
    callAiStream (userMessage, options = {}) {
      const {
        chatId = this._aiChatId || (this._aiChatId = moment().format('YYYYMMDDHHmmss')),
        model = '',
        prompt = '',
        useContext = false,
        onChunk,
        onEnd,
        onError
      } = options

      let currentContent = ''
      let stopConnection = null

      this.isStreaming = true
      this.streamEnded = false

      stopConnection = startEventStreamPOST(
        '/his-web/api/af-his/ai/chat/stream',
        {
          userMessage,
          chatId,
          model,
          prompt,
          useContext
        },
        {'Content-Type': 'application/json'},
        (data, type) => {
          if (type === 'additionalInfo' || type === 'sourceInfo') {
            return
          }
          let textChunk = ''
          if (typeof data === 'object' && data !== null) {
            const choices = data.choices
            if (Array.isArray(choices) && choices.length > 0) {
              const delta = choices[0].delta
              if (delta && typeof delta.content === 'string') {
                textChunk = delta.content
              }
            }
          } else if (typeof data === 'string') {
            textChunk = data
          }

          if (textChunk) {
            // const beforeLen = currentContent.length
            currentContent += textChunk
            // 同步更新 internalMarkdown 以触发视图更新
            this.internalMarkdown = currentContent
            onChunk?.(textChunk, currentContent)
            this.scrollToBottom()
          }
        },
        (error) => {
          this.isStreaming = false
          this.streamEnded = true
          onError?.(error.message || '请求失败')
        },
        () => {
          this.isStreaming = false
          this.streamEnded = true
          onEnd?.(currentContent)
          this.scrollToBottom()
        }
      )

      this._currentStreamConnection = {
        stop: () => {
          stopConnection?.()
        },
        getContent: () => currentContent
      }

      return this._currentStreamConnection
    },
    /**
     * 停止当前的流式请求
     */
    stopStream() {
      this._currentStreamConnection?.stop()
      this.isStreaming = false
      this.streamEnded = true
    },
    /**
     * 处理按钮点击
     */
    handleButtonClick() {
      if (this.buttonConfig.eventDisabled) {
        return
      }

      if (this.isStreaming) {
        this.stopStream()
        this.$emit('stop-stream')
      } else {
        this.streamEnded = false
        const startEvent = this.buttonConfig.startClickEventName || 'start-stream'
        console.log('[XMarkdownViewer] 触发开始事件:', startEvent)
        this.$emit(startEvent)
      }
    },
    /**
     * 获取当前累积的内容
     */
    getStreamContent() {
      return this._currentStreamConnection?.getContent() || ''
    },
    /**
     * 滚动到底部
     */
    scrollToBottom() {
      if (this.$refs.containerRef) {
        this.$nextTick(() => {
          const container = this.$refs.containerRef
          container.scrollTop = container.scrollHeight
        })
      }
    },
    /**
     * 重置流状态
     */
    resetStreamState() {
      this.isStreaming = false
      this.streamEnded = false
    },
    /**
     * 加载按钮配置
     */
    loadButtonConfig(configName) {
      if (!configName) {
        console.warn('[XMarkdownViewer] queryParamsName 为空，跳过加载')
        this._emitConfigEnd()
        return
      }
      console.log('[XMarkdownViewer] 开始加载按钮配置:', configName)

      // 优先使用传入的 serviceName，否则使用 'af-his' 确保获取到正确的配置
      const serviceName = this.serviceName || 'af-his'
      console.log('[XMarkdownViewer] 使用服务名:', serviceName)

      getConfigByName(configName, serviceName, (res) => {
        console.log('[XMarkdownViewer] 配置加载回调, res:', res)
        if (!res) {
          console.warn('[XMarkdownViewer] 按钮配置未找到:', configName)
          this._emitConfigEnd()
          return
        }
        console.log('[XMarkdownViewer] 应用配置:', res)
        this.buttonConfig = {
          ...res
        }
        if (typeof res.showButtonInitially === 'boolean') {
          this.showButtonInitially = res.showButtonInitially
        }
        console.log('[XMarkdownViewer] buttonConfig 已更新:', this.buttonConfig)
        this._emitConfigEnd()
      }, this.env === 'dev')
    },
    /**
     * 获取按钮图标
     */
    getButtonIcon() {
      if (this.isStreaming) {
        return this.buttonConfig.stopIcon || 'pause-circle'
      } else if (this.streamEnded) {
        return this.buttonConfig.stoppedIcon || 'check-circle'
      } else {
        return this.buttonConfig.startIcon || 'play-circle'
      }
    },
    /**
     * 获取按钮文字
     */
    getButtonText() {
      if (this.isStreaming) {
        return this.buttonConfig.stopText || '停止'
      } else if (this.streamEnded) {
        return this.buttonConfig.startText || '开始'
      } else {
        return this.buttonConfig.startText || '开始'
      }
    }
  },
  mounted() {
    this.containerRef = this.$refs.containerRef
  }
}
</script>

<style lang="less" scoped>
.x-markdown-viewer-wrapper {
  width: 100%;
  display: flex;
  flex-direction: column;
  opacity: 1;
  background: #FFFFFF;
  box-sizing: border-box;
  border: 1px solid #E5E9F0;
  padding: 5px;

  .stream-control-bar {
    display: flex;
    justify-content: flex-end;
    padding: 5px;
    margin-bottom: 5px;
    border-bottom: 1px solid #e8e8e8;

    .stream-btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      width: 110px;
      height: 32px;
      padding: 0;
      font-family: "Source Han Sans";
      font-size: 16px;
      font-weight: normal;
      letter-spacing: 0em;
      line-height: normal;
      font-feature-settings: "kern" on;
      color: #5D5C5C;
      opacity: 1;
      background: #FFFFFF;
      border: 1px solid #E5E9F0;
      border-radius: 6px;
      box-sizing: border-box;
      margin-right: 5px;

      &.is-streaming {
        background: #fff2f0;
        border-color: #ff4d4f;
        color: #ff4d4f;

        &:hover {
          background: #fff1f0;
          border-color: #ff7875;
          color: #ff7875;
        }
      }

    }
  }

  .x-markdown-viewer {
    width: 100%;
    max-height: 350px;
    overflow-y: auto;
    overflow-x: hidden;
  }
}

.markdown-body {
  font-family: "Source Han Sans";
  font-size: 16px;
  font-weight: normal;
  line-height: normal;
  text-align: justify;
  letter-spacing: 0em;
  font-feature-settings: "kern" on;
  color: #313131;
  word-break: break-word;
}

.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
  margin-top: 16px;
  margin-bottom: 8px;
  font-weight: 600;
}

.markdown-body p {
  margin: 0 0 12px;
}

.markdown-body ul,
.markdown-body ol {
  padding-left: 2em;
  margin: 0 0 12px;
}

.markdown-body pre {
  background-color: #f6f8fa;
  border-radius: 6px;
  padding: 12px;
  overflow: auto;
}

.markdown-body code {
  background-color: #f6f8fa;
  border-radius: 3px;
  padding: 0.2em 0.4em;
  font-size: 85%;
}
</style>
