/**
 * AssistantText 路由标签悬停显示单元测试 (U5)。
 *
 * 验证场景：
 * - 默认状态路由 tag 不可见
 * - 鼠标悬停助手消息时 tag 淡入显示
 * - 鼠标移开时 tag 淡出
 * - 无 matched_skill 的消息悬停时也不显示 tag
 * - 淡入淡出过渡平滑无闪烁（v-show 保留 DOM，无重挂载）
 *
 * 说明：happy-dom 不计算 CSS transition，因此"淡入/淡出"通过
 * `assistant-text__routing--visible` class 的增删来断言；"平滑无闪烁"
 * 通过断言 DOM 节点恒定（v-show 而非 v-if，hover 前后同一节点）验证。
 */

import { afterEach, describe, expect, it, vi } from 'vitest'
import { createApp, h, nextTick, type App } from 'vue'
import AssistantText from '@/components/chat/messages/AssistantText.vue'
import type { IChatMessage } from '@/api/types'

// ── Mocks ────────────────────────────────────────────────────────────
// 路由标签可见性逻辑不依赖 markdown 渲染，mock 掉重量级依赖以隔离逻辑、
// 加快测试。ant-design-vue 的 Tag/Spin 与图标保留真实加载（happy-dom 可渲染）。

vi.mock('markdown-it', () => {
  const md = {
    render: (s: string) => s,
    utils: { escapeHtml: (s: string) => s },
    renderer: { rules: {} as Record<string, unknown> },
  }
  return { default: vi.fn(() => md) }
})

vi.mock('dompurify', () => ({
  default: { sanitize: (html: string) => html },
}))

vi.mock('highlight.js/lib/core', () => ({
  default: {
    registerLanguage: vi.fn(),
    getLanguage: vi.fn(() => null),
    highlight: vi.fn(() => ({ value: '' })),
  },
}))

// ── Fixtures & helpers ───────────────────────────────────────────────

function makeMessage(overrides: Partial<IChatMessage> = {}): IChatMessage {
  return {
    id: 'msg-1',
    role: 'assistant',
    content: '你好',
    timestamp: '2026-07-01T00:00:00.000Z',
    status: 'completed',
    matched_skill: 'react',
    routing_method: 'semantic',
    confidence: 0.9,
    ...overrides,
  }
}

interface Mounted {
  container: HTMLElement
  root: HTMLElement
  app: App
  unmount: () => void
}

function mountAssistantText(message: IChatMessage): Mounted {
  const container = document.createElement('div')
  document.body.appendChild(container)
  const app = createApp({
    render: () => h(AssistantText, { message }),
  })
  app.mount(container)
  const root = container.querySelector('.assistant-text') as HTMLElement
  return { container, root, app, unmount: () => { app.unmount(); container.remove() } }
}

function hover(el: HTMLElement): void {
  el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: false }))
}

function leave(el: HTMLElement): void {
  el.dispatchEvent(new MouseEvent('mouseleave', { bubbles: false }))
}

function getRouting(container: HTMLElement): HTMLElement {
  return container.querySelector('.assistant-text__routing') as HTMLElement
}

// ── Tests ────────────────────────────────────────────────────────────

describe('AssistantText — 路由标签悬停显示 (U5)', () => {
  let mounted: Mounted | null = null

  afterEach(() => {
    mounted?.unmount()
    mounted = null
  })

  it('默认状态路由 tag 不可见', () => {
    mounted = mountAssistantText(makeMessage())
    const routing = getRouting(mounted.container)
    // v-show 保留 DOM（非 v-if），但默认 opacity:0 → 无 --visible class
    expect(routing).not.toBeNull()
    expect(routing.classList.contains('assistant-text__routing--visible')).toBe(false)
  })

  it('鼠标悬停助手消息时 tag 淡入显示', async () => {
    mounted = mountAssistantText(makeMessage())
    const routing = getRouting(mounted.container)
    expect(routing.classList.contains('assistant-text__routing--visible')).toBe(false)

    hover(mounted.root)
    await nextTick()

    expect(routing.classList.contains('assistant-text__routing--visible')).toBe(true)
  })

  it('鼠标移开时 tag 淡出', async () => {
    mounted = mountAssistantText(makeMessage())
    hover(mounted.root)
    await nextTick()
    const routing = getRouting(mounted.container)
    expect(routing.classList.contains('assistant-text__routing--visible')).toBe(true)

    leave(mounted.root)
    await nextTick()

    expect(routing.classList.contains('assistant-text__routing--visible')).toBe(false)
  })

  it('无 matched_skill 的消息悬停时也不显示 tag', async () => {
    mounted = mountAssistantText(makeMessage({ matched_skill: undefined }))
    const routing = getRouting(mounted.container)
    // v-show=false → display:none
    expect(routing.style.display).toBe('none')

    hover(mounted.root)
    await nextTick()

    // 即使悬停，因无路由信息仍隐藏
    expect(routing.style.display).toBe('none')
  })

  it('淡入淡出过渡平滑无闪烁（v-show 保留 DOM，无重挂载）', async () => {
    mounted = mountAssistantText(makeMessage())
    const routingBefore = getRouting(mounted.container)

    hover(mounted.root)
    await nextTick()
    const routingHovered = getRouting(mounted.container)
    // hover 前后同一节点 → 未重挂载
    expect(routingHovered).toBe(routingBefore)

    leave(mounted.root)
    await nextTick()
    const routingLeft = getRouting(mounted.container)
    expect(routingLeft).toBe(routingBefore)
  })
})
