/**
 * Unit tests for bubbleUtils — pure functions extracted from MessageShell /
 * ChatMessage / UserBubble to support testing of key decisions:
 *
 *   - isCardBearingType (F4-A): card-bearing assistant types skip bubble chrome
 *   - isAssistantBubbleEmpty (G1): empty content detection replaces :empty selector
 *   - isPlainUserText (U4): plain text vs file-attachment / @board/@team command
 *
 * These cover the P0/P1 testing gaps flagged by ce-code-review: G1 is a P0
 * decision (:empty never matches), F4-A is a P0 regression guard (double
 * bubble), U4 is a P0 feature (dark bubble only for plain text).
 */

import { describe, expect, it } from 'vitest'
import {
  isCardBearingType,
  isAssistantBubbleEmpty,
  isPlainUserText,
} from '@/components/chat/helpers/bubbleUtils'
import type { IChatMessage } from '@/api/types'

function makeMsg(overrides: Partial<IChatMessage> = {}): IChatMessage {
  return {
    id: 'm1',
    role: 'assistant',
    content: '',
    timestamp: '2026-07-01T00:00:00Z',
    ...overrides,
  }
}

// ── isCardBearingType (F4-A) ────────────────────────────────────────

describe('isCardBearingType — F4-A card-bearing exclusion', () => {
  const cardBearingTypes = [
    'board_conclusion',
    'team_plan',
    'debate_started',
    'debate_argument',
    'debate_summary',
    'debate_resolved',
    'collaboration_graph',
    'review_result',
    'risk_flagged',
    'error',
  ]

  it.each(cardBearingTypes)('returns true for card-bearing type "%s"', (type) => {
    expect(isCardBearingType(type)).toBe(true)
  })

  it('returns false for plain assistant type', () => {
    expect(isCardBearingType('assistant')).toBe(false)
  })

  it('returns false for user type', () => {
    expect(isCardBearingType('user')).toBe(false)
  })

  it('returns false for milestone type (not card-bearing)', () => {
    expect(isCardBearingType('milestone')).toBe(false)
  })

  it('returns false for board_banner / board_speech / board_summary (partial chrome, no own bubble chrome)', () => {
    // These use BoardRoundCard which relies on the assistant bubble chrome,
    // so they must NOT be excluded.
    expect(isCardBearingType('board_banner')).toBe(false)
    expect(isCardBearingType('board_speech')).toBe(false)
    expect(isCardBearingType('board_summary')).toBe(false)
  })

  it('returns false for undefined type', () => {
    expect(isCardBearingType(undefined)).toBe(false)
  })

  it('returns false for empty string', () => {
    expect(isCardBearingType('')).toBe(false)
  })

  it('returns false for unknown type', () => {
    expect(isCardBearingType('unknown_type')).toBe(false)
  })
})

// ── isAssistantBubbleEmpty (G1) ─────────────────────────────────────

describe('isAssistantBubbleEmpty — G1 empty content detection', () => {
  it('returns true for assistant with no content, no thinking, no tool_calls', () => {
    const msg = makeMsg({ role: 'assistant', content: '' })
    expect(isAssistantBubbleEmpty(msg)).toBe(true)
  })

  it('returns true for assistant with undefined content and thinking', () => {
    const msg = makeMsg({ role: 'assistant', content: undefined, thinking: undefined })
    expect(isAssistantBubbleEmpty(msg)).toBe(true)
  })

  it('returns true for assistant with empty tool_calls array', () => {
    const msg = makeMsg({ role: 'assistant', content: '', tool_calls: [] })
    expect(isAssistantBubbleEmpty(msg)).toBe(true)
  })

  it('returns false for assistant with content', () => {
    const msg = makeMsg({ role: 'assistant', content: 'hello' })
    expect(isAssistantBubbleEmpty(msg)).toBe(false)
  })

  it('returns false for assistant with thinking content', () => {
    const msg = makeMsg({ role: 'assistant', content: '', thinking: '思考中' })
    expect(isAssistantBubbleEmpty(msg)).toBe(false)
  })

  it('returns false for assistant with non-empty tool_calls', () => {
    const msg = makeMsg({
      role: 'assistant',
      content: '',
      tool_calls: [{ id: 't1', name: 'search', arguments: '{}' } as never],
    })
    expect(isAssistantBubbleEmpty(msg)).toBe(false)
  })

  it('returns false for user role even when content is empty', () => {
    const msg = makeMsg({ role: 'user', content: '' })
    expect(isAssistantBubbleEmpty(msg)).toBe(false)
  })

  it('returns false for user role with content', () => {
    const msg = makeMsg({ role: 'user', content: 'hi' })
    expect(isAssistantBubbleEmpty(msg)).toBe(false)
  })
})

// ── isPlainUserText (U4) ────────────────────────────────────────────

describe('isPlainUserText — U4 plain text detection', () => {
  it('returns true for plain text', () => {
    expect(isPlainUserText('你好，帮我重构这个函数')).toBe(true)
  })

  it('returns true for empty string (no command, no file)', () => {
    expect(isPlainUserText('')).toBe(true)
  })

  it('returns true for text with @ mention that is not board/team', () => {
    expect(isPlainUserText('@user hello')).toBe(true)
  })

  it('returns false for @board command', () => {
    expect(isPlainUserText('@board 讨论技术选型')).toBe(false)
  })

  it('returns false for @team command', () => {
    expect(isPlainUserText('@team 完成需求分析')).toBe(false)
  })

  it('returns false for @board with expert list', () => {
    expect(isPlainUserText('@board:expert1,expert2 讨论架构')).toBe(false)
  })

  it('returns false for @team with template', () => {
    expect(isPlainUserText('@team:dev_team 实现登录功能')).toBe(false)
  })

  it('returns false for @board with rounds param', () => {
    expect(isPlainUserText('@board rounds=3 讨论方案')).toBe(false)
  })

  it('returns false for file attachment markdown', () => {
    expect(isPlainUserText('[文件] [report.pdf](https://example.com/report.pdf)')).toBe(false)
  })

  it('returns true for text containing @board in the middle (not a prefix)', () => {
    // Only prefix @board/@team commands are recognized; mid-text @ is plain.
    expect(isPlainUserText('请帮我 @board 这个问题')).toBe(true)
  })

  it('returns true for multiline plain text', () => {
    expect(isPlainUserText('第一行\n第二行\n第三行')).toBe(true)
  })
})
