/**
 * Unit tests for U6 — UserBubble hover actions (copy/delete/refill).
 *
 * Project convention (see vitest.config.ts): unit tests are pure-TS, no Vue
 * component mounting (no @vue/test-utils dependency). So we test the
 * testable logic that drives the UI:
 *
 *   1. `nextMessageIsAssistant` — the pure selector that decides whether the
 *      delete button is disabled (scenario: "删除有助手回复跟随的用户消息 →
 *      删除按钮 disabled").
 *   2. `chatStore.deleteMessage` — frontend-only removal; verifies the
 *      message disappears from the list while the server copy is untouched
 *      (scenarios: "确认后消息从列表消失", "仅前端隐藏，不删服务端副本").
 *   3. `chatStore` refill channel (`refillText`/`setRefillText`/`clearRefillText`)
 *      — verifies the backfill value round-trips and that a second click on
 *      the same message re-triggers the watcher via the "" -> content
 *      transition (scenario: "回填按钮点击后输入框显示消息文本").
 *
 * Interaction-only scenarios (hover/focus/touch reveal, clipboard write,
 * popconfirm open/cancel) are thin wiring over library calls and clipboard
 * APIs; per the lazy-senior rule, trivial one-liners carry no dedicated test.
 */

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'

// Match the store-test mocking pattern from chat-phase.test.ts so importing
// the store doesn't touch the network or sibling stores' dependencies.
vi.mock('@/api/client', () => ({
  apiClient: {
    get: vi.fn(),
    post: vi.fn(),
    put: vi.fn(),
    delete: vi.fn(),
    patch: vi.fn(),
    deleteConversation: vi.fn(),
  },
}))
vi.mock('@/stores/team', () => ({
  useTeamStore: vi.fn(() => null),
}))
vi.mock('@/stores/documents', () => ({
  useDocumentsStore: vi.fn(() => null),
}))
vi.mock('@/stores/calendar', () => ({
  useCalendarStore: vi.fn(() => null),
}))
vi.mock('@/api/documents', () => ({
  isDocumentMeta: vi.fn(),
}))

import {
  nextMessageIsAssistant,
  useChatStore,
} from '@/stores/chatStore'
import type { IChatMessage, IConversation } from '@/api/types'

function makeMsg(
  id: string,
  role: 'user' | 'assistant',
  content = 'x',
): IChatMessage {
  return { id, role, content, timestamp: '2026-07-01T00:00:00Z' }
}

function makeConv(id: string, messages: IChatMessage[]): IConversation {
  return {
    id,
    title: '对话',
    messages,
    created_at: '2026-07-01T00:00:00Z',
    updated_at: '2026-07-01T00:00:00Z',
  }
}

describe('nextMessageIsAssistant — delete-disabled selector (U6)', () => {
  it('returns true when an assistant message directly follows the user msg', () => {
    const msgs = [makeMsg('u1', 'user'), makeMsg('a1', 'assistant')]
    expect(nextMessageIsAssistant(msgs, 'u1')).toBe(true)
  })

  it('returns false when the next message is another user message', () => {
    const msgs = [makeMsg('u1', 'user'), makeMsg('u2', 'user')]
    expect(nextMessageIsAssistant(msgs, 'u1')).toBe(false)
  })

  it('returns false when the user message is the last one (no reply yet)', () => {
    const msgs = [makeMsg('u1', 'user')]
    expect(nextMessageIsAssistant(msgs, 'u1')).toBe(false)
  })

  it('returns false when the msgId is not present', () => {
    const msgs = [makeMsg('u1', 'user'), makeMsg('a1', 'assistant')]
    expect(nextMessageIsAssistant(msgs, 'missing')).toBe(false)
  })

  it('returns false for an empty message list', () => {
    expect(nextMessageIsAssistant([], 'u1')).toBe(false)
  })
})

describe('chatStore.deleteMessage — frontend-only hide (U6)', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
  })

  it('removes the message from the current conversation (frontend hide)', async () => {
    const { apiClient } = await import('@/api/client')
    const store = useChatStore()
    const conv = makeConv('c1', [
      makeMsg('u1', 'user', 'hello'),
      makeMsg('a1', 'assistant', 'hi'),
    ])
    store.conversations = [conv]
    store.currentConversationId = 'c1'

    store.deleteMessage('c1', 'u1')

    expect(store.currentMessages.map((m) => m.id)).toEqual(['a1'])
    // Server copy preserved: deleteMessage must not issue any API call.
    expect(apiClient.deleteConversation).not.toHaveBeenCalled()
  })

  it('leaves the assistant reply intact when deleting the user message', async () => {
    const store = useChatStore()
    store.conversations = [
      makeConv('c1', [makeMsg('u1', 'user'), makeMsg('a1', 'assistant')]),
    ]
    store.currentConversationId = 'c1'

    store.deleteMessage('c1', 'u1')

    expect(store.currentMessages).toHaveLength(1)
    expect(store.currentMessages[0].role).toBe('assistant')
  })

  it('is a no-op for an unknown conversation id', async () => {
    const store = useChatStore()
    store.conversations = [makeConv('c1', [makeMsg('u1', 'user')])]

    store.deleteMessage('does-not-exist', 'u1')

    expect(store.conversations[0].messages).toHaveLength(1)
  })

  it('is a no-op for an unknown message id (list unchanged)', async () => {
    const store = useChatStore()
    store.conversations = [makeConv('c1', [makeMsg('u1', 'user')])]

    store.deleteMessage('c1', 'missing')

    expect(store.conversations[0].messages.map((m) => m.id)).toEqual(['u1'])
  })
})

describe('chatStore refill channel (U6)', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
  })

  it('starts empty, setRefillText publishes the value, clearRefillText resets', async () => {
    const store = useChatStore()
    expect(store.refillText).toBe('')

    store.setRefillText('帮我重构这个函数')
    expect(store.refillText).toBe('帮我重构这个函数')

    store.clearRefillText()
    expect(store.refillText).toBe('')
  })

  it('after consume-then-clear, a second set on the same content is a real "" -> content change (re-fires watcher)', async () => {
    // ChatInput consumes refillText then clears it. For a second click on the
    // SAME message to re-trigger ChatInput's watcher, refillText must return
    // to "" after consume so the next setRefillText is a genuine value change.
    // This pins that contract (Vue fires watchers on real changes).
    const store = useChatStore()

    store.setRefillText('A') // first refill click
    expect(store.refillText).toBe('A')
    store.clearRefillText() // ChatInput consume-then-clear
    expect(store.refillText).toBe('')

    store.setRefillText('A') // second click on same msg — real change "" -> "A"
    expect(store.refillText).toBe('A')
  })
})
