import { flushPromises, mount } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi, type Mocked } from 'vitest'

vi.mock('@/api/graph', () => ({
  graphApi: {
    get: vi.fn(),
    build: vi.fn(),
    getEntityContext: vi.fn(),
  },
}))

vi.mock('./BrainGraphCanvas.vue', async () => {
  const { defineComponent, h } = await import('vue')

  return {
    default: defineComponent({
      name: 'BrainGraphCanvas',
      props: {
        nodes: { type: Array, required: true },
        edges: { type: Array, required: true },
        selectedNodeId: { type: String, default: null },
        getColor: { type: Function, required: true },
      },
      emits: ['select-node', 'clear-selection'],
      setup(props: { nodes: Array<{ id?: string }>; edges: unknown[]; selectedNodeId?: string | null }, { emit, expose }) {
        const zoomBy = vi.fn()
        const resetViewport = vi.fn()

        expose({ zoomBy, resetViewport })

        return () => h('div', { 'data-testid': 'brain-canvas-stub' }, [
          h('button', {
            'data-testid': 'brain-canvas-select-first',
            onClick: () => emit('select-node', props.nodes[0]?.id),
          }, 'select-first'),
          h('button', {
            'data-testid': 'brain-canvas-clear',
            onClick: () => emit('clear-selection'),
          }, 'clear-selection'),
          h('span', { 'data-testid': 'brain-canvas-node-count' }, String(props.nodes.length)),
          h('span', { 'data-testid': 'brain-canvas-edge-count' }, String(props.edges.length)),
          h('span', { 'data-testid': 'brain-canvas-selected' }, props.selectedNodeId || 'none'),
        ])
      },
    }),
  }
})

import GraphProjection from './GraphProjection.vue'
import { graphApi } from '@/api/graph'

const mockedGraphApi = graphApi as Mocked<typeof graphApi>

const liveGraphPayload = {
  data: {
    nodes: [
      {
        id: 'node-1',
        name: 'Jarvis 架构手册',
        type: 'knowledge',
        description: '系统架构文档',
        importance: 0.95,
        created_at: '2026-03-21T09:30:00Z',
      },
      {
        id: 'node-2',
        name: '改版需求确认会话',
        type: 'chat',
        description: '需求澄清',
        importance: 0.82,
        created_at: '2026-03-26T03:18:00Z',
      },
      {
        id: 'node-3',
        name: '知识工作台案例观察',
        type: 'forum',
        description: '案例观察',
        importance: 0.62,
        created_at: '2026-03-19T11:00:00Z',
      },
    ],
    edges: [
      {
        id: 'edge-1',
        source: 'node-1',
        target: 'node-2',
        relation: '支撑讨论',
      },
      {
        id: 'edge-2',
        source: 'node-2',
        target: 'node-3',
        relation: '关联观察',
      },
    ],
    stats: {
      node_count: 3,
      edge_count: 2,
    },
  },
}

function mountProjection(props: Record<string, unknown> = {}) {
  return mount(GraphProjection, {
    props,
    global: {
      stubs: {
        RouterLink: {
          template: '<a><slot /></a>',
        },
      },
    },
  })
}

describe('GraphProjection', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockedGraphApi.get.mockResolvedValue(liveGraphPayload as never)
    mockedGraphApi.build.mockResolvedValue({ data: { message: 'ok' } } as never)
    mockedGraphApi.getEntityContext.mockResolvedValue({ data: { context: 'context' } } as never)
  })

  it('renders the whiteboard shell with graph stage and idle lock prompt', async () => {
    const wrapper = mountProjection({
      fullscreen: true,
      showOpenFullView: false,
    })

    await flushPromises()

    expect(wrapper.get('[data-testid="brain-projection-shell"]')).toBeTruthy()
    expect(wrapper.get('[data-testid="brain-telemetry-rail"]')).toBeTruthy()
    expect(wrapper.get('[data-testid="brain-graph-card"]')).toBeTruthy()
    expect(wrapper.get('[data-testid="brain-whiteboard-stage"]')).toBeTruthy()
    expect(wrapper.get('[data-testid="brain-detail-empty"]')).toBeTruthy()
    expect(wrapper.find('[data-testid="brain-detail-panel"]').exists()).toBe(false)
    expect(wrapper.get('[data-testid="brain-canvas-stub"]')).toBeTruthy()
  })

  it('shows the open full view action only when enabled', async () => {
    const hiddenWrapper = mountProjection({ showOpenFullView: false })
    await flushPromises()
    expect(hiddenWrapper.text()).not.toContain('全屏查看')

    const visibleWrapper = mountProjection({ showOpenFullView: true })
    await flushPromises()
    expect(visibleWrapper.text()).toContain('全屏查看')
  })

  it('falls back to the demo graph without rendering a mock warning ribbon', async () => {
    mockedGraphApi.get.mockResolvedValue({
      data: {
        nodes: [],
        edges: [],
        stats: {
          node_count: 0,
          edge_count: 0,
        },
      },
    } as never)

    const wrapper = mountProjection()

    await flushPromises()

    expect(wrapper.text()).not.toContain('MOCK SIGNAL')
    expect(wrapper.text()).not.toContain('Mock')
    expect(wrapper.get('[data-testid="brain-telemetry-clusters"]')).toBeTruthy()
  })

  it('selects a node from the renderer and opens the floating inspector', async () => {
    const wrapper = mountProjection()
    await flushPromises()

    await wrapper.get('[data-testid="brain-canvas-select-first"]').trigger('click')
    await flushPromises()

    expect(mockedGraphApi.getEntityContext).toHaveBeenCalledWith('Jarvis 架构手册')
    expect(wrapper.find('[data-testid="brain-detail-empty"]').exists()).toBe(false)
    expect(wrapper.get('[data-testid="brain-detail-panel"]').text()).toContain('Jarvis 架构手册')
    expect(wrapper.text()).toContain('INGEST SIGNAL')
    expect(wrapper.text()).toContain('ENTITY BRIEF')
    expect(wrapper.get('[data-testid="brain-canvas-selected"]').text()).toContain('node-1')
  })

  it('filters visible graph data by importance and shows empty state when no nodes match', async () => {
    const wrapper = mountProjection()
    await flushPromises()

    await wrapper.get('[data-testid="brain-filter-importance-high"]').trigger('click')
    await flushPromises()

    expect(wrapper.get('[data-testid="brain-canvas-node-count"]').text()).toContain('1')
    expect(wrapper.get('[data-testid="brain-canvas-edge-count"]').text()).toContain('0')

    mockedGraphApi.get.mockResolvedValue({
      data: {
        nodes: [
          {
            id: 'node-low',
            name: '低优先级线索',
            type: 'forum',
            description: 'low',
            importance: 0.4,
            created_at: '2026-03-19T11:00:00Z',
          },
        ],
        edges: [],
        stats: {
          node_count: 1,
          edge_count: 0,
        },
      },
    } as never)

    const lowOnlyWrapper = mountProjection()
    await flushPromises()
    await lowOnlyWrapper.get('[data-testid="brain-filter-importance-high"]').trigger('click')
    await flushPromises()

    expect(lowOnlyWrapper.get('[data-testid="brain-empty-state"]')).toBeTruthy()
  })

  it('switches to demo preview mode and renders richer simulated graph data', async () => {
    const wrapper = mountProjection()
    await flushPromises()

    expect(wrapper.get('[data-testid="brain-canvas-node-count"]').text()).toContain('3')
    expect(wrapper.get('[data-testid="brain-preview-badge"]').text()).toContain('LIVE GRID')

    await wrapper.get('[data-testid="brain-preview-toggle"]').trigger('click')
    await flushPromises()

    expect(wrapper.get('[data-testid="brain-preview-badge"]').text()).toContain('DEMO GRID')
    expect(wrapper.get('[data-testid="brain-canvas-node-count"]').text()).toContain('50')
    expect(wrapper.get('[data-testid="brain-canvas-edge-count"]').text()).toContain('76')
  })

  it('uses local summary when selecting a demo preview node', async () => {
    const wrapper = mountProjection()
    await flushPromises()

    await wrapper.get('[data-testid="brain-preview-toggle"]').trigger('click')
    await flushPromises()

    await wrapper.get('[data-testid="brain-canvas-select-first"]').trigger('click')
    await flushPromises()

    expect(mockedGraphApi.getEntityContext).not.toHaveBeenCalledWith('Orchestrator Prime')
    expect(wrapper.get('[data-testid="brain-detail-panel"]').text()).toContain('Orchestrator Prime')
    expect(wrapper.text()).toContain('指挥中枢')
    expect(wrapper.text()).toContain('负责汇总知识、会话、论坛与排程信号')
    expect(wrapper.get('[data-testid="brain-canvas-selected"]').text()).toContain('demo-core-orchestrator')
  })

  it('returns from demo preview mode back to live graph data', async () => {
    const wrapper = mountProjection()
    await flushPromises()

    expect(wrapper.get('[data-testid="brain-canvas-node-count"]').text()).toContain('3')

    await wrapper.get('[data-testid="brain-preview-toggle"]').trigger('click')
    await flushPromises()
    expect(wrapper.get('[data-testid="brain-preview-badge"]').text()).toContain('DEMO GRID')
    expect(wrapper.get('[data-testid="brain-canvas-node-count"]').text()).toContain('50')

    await wrapper.get('[data-testid="brain-preview-toggle"]').trigger('click')
    await flushPromises()

    expect(wrapper.get('[data-testid="brain-preview-badge"]').text()).toContain('LIVE GRID')
    expect(wrapper.get('[data-testid="brain-canvas-node-count"]').text()).toContain('3')
    expect(wrapper.get('[data-testid="brain-canvas-edge-count"]').text()).toContain('2')
  })

  it('clears the lock when the inspector close button is pressed', async () => {
    const wrapper = mountProjection()
    await flushPromises()

    await wrapper.get('[data-testid="brain-canvas-select-first"]').trigger('click')
    await flushPromises()
    expect(wrapper.get('[data-testid="brain-detail-panel"]').text()).toContain('Jarvis 架构手册')

    await wrapper.get('[data-testid="brain-inspector-close"]').trigger('click')
    await flushPromises()

    expect(wrapper.find('[data-testid="brain-detail-panel"]').exists()).toBe(false)
    expect(wrapper.get('[data-testid="brain-detail-empty"]').text()).toContain('WAITING FOR TARGET LOCK')
  })

  it('clears selection when the renderer emits clear-selection', async () => {
    const wrapper = mountProjection()
    await flushPromises()

    await wrapper.get('[data-testid="brain-canvas-select-first"]').trigger('click')
    await flushPromises()
    expect(wrapper.find('[data-testid="brain-detail-panel"]').exists()).toBe(true)

    await wrapper.get('[data-testid="brain-canvas-clear"]').trigger('click')
    await flushPromises()

    expect(wrapper.find('[data-testid="brain-detail-panel"]').exists()).toBe(false)
    expect(wrapper.find('[data-testid="brain-detail-empty"]').exists()).toBe(true)
  })
})
