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

const mocks = vi.hoisted(() => ({
  list: vi.fn(),
  create: vi.fn(),
  update: vi.fn(),
  delete: vi.fn(),
  bootstrapBuiltin: vi.fn(),
}))

vi.mock('@/api/skill', () => ({
  skillApi: {
    list: mocks.list,
    create: mocks.create,
    update: mocks.update,
    delete: mocks.delete,
    bootstrapBuiltin: mocks.bootstrapBuiltin,
  },
}))

import SkillsPage from './index.vue'

const skillFixtures = [
  {
    id: 'skill-schedule-1',
    name: 'Priority Router',
    description: 'Aligns planner priorities.',
    instructions: 'Prioritize schedule risks.',
    agent_type: 'schedule_planner',
    tools: ['calendar', 'tasks'],
    required_context: [],
    output_format: null,
    visibility: 'private' as const,
    is_builtin: true,
    team_id: null,
    is_active: true,
    owner_id: 'user-1',
    created_at: '2026-03-26T00:00:00Z',
    updated_at: '2026-03-26T00:00:00Z',
  },
  {
    id: 'skill-analyst-1',
    name: 'Trend Lens',
    description: 'Summarizes movement signals.',
    instructions: 'Summarize the latest trend shifts.',
    agent_type: 'analyst',
    tools: ['database'],
    required_context: [],
    output_format: null,
    visibility: 'team' as const,
    is_builtin: false,
    team_id: null,
    is_active: true,
    owner_id: 'user-1',
    created_at: '2026-03-26T00:00:00Z',
    updated_at: '2026-03-26T00:00:00Z',
  },
]

describe('skills page ability center', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mocks.list.mockResolvedValue({ data: skillFixtures })
    mocks.create.mockResolvedValue({ data: skillFixtures[0] })
    mocks.update.mockResolvedValue({ data: skillFixtures[0] })
    mocks.delete.mockResolvedValue({})
    mocks.bootstrapBuiltin.mockResolvedValue({ data: skillFixtures })
    vi.stubGlobal('navigator', {
      clipboard: {
        writeText: vi.fn().mockResolvedValue(undefined),
      },
    })
    vi.stubGlobal('confirm', vi.fn(() => true))
    vi.stubGlobal('matchMedia', vi.fn().mockImplementation(() => ({
      matches: false,
      media: '(prefers-reduced-motion: reduce)',
      onchange: null,
      addListener: vi.fn(),
      removeListener: vi.fn(),
      addEventListener: vi.fn(),
      removeEventListener: vi.fn(),
      dispatchEvent: vi.fn(),
    })))
  })

  it('renders the ability center title and preserves the create skill action', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    expect(wrapper.get('[data-testid="skills-page-title"]').text()).toContain('能力中心')
    expect(wrapper.text()).not.toContain('技能中心')
    expect(wrapper.get('[data-testid="skills-create-button"]').text()).toContain('新建技能')
  })

  it('removes the legacy refresh action from the header', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    expect(wrapper.find('[data-testid="skills-refresh-button"]').exists()).toBe(false)
  })

  it('shows an MCP action in the header', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    expect(wrapper.get('[data-testid="skills-mcp-button"]').text()).toContain('MCP')
    expect(wrapper.find('[data-testid="skills-create-button"]').exists()).toBe(true)
  })

  it('shows builtin badges in the table without the legacy overview chips', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    expect(wrapper.find('[data-testid="skills-overview"]').exists()).toBe(false)

    const builtinBadges = wrapper.findAll('[data-testid="builtin-skill-badge"]')
    expect(builtinBadges).toHaveLength(1)
    expect(builtinBadges[0].text()).toContain('内置')
    expect(wrapper.text()).toContain('Priority Router')
    expect(wrapper.text()).toContain('Trend Lens')
  })

  it('renders the new table-first console layout with the detail drawer hidden by default', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    expect(wrapper.find('[data-testid="skills-table"]').exists()).toBe(true)
    expect(wrapper.find('[data-testid="skills-detail-panel"]').exists()).toBe(false)
    expect(wrapper.get('[data-testid="skills-metrics-strip"]').text()).toContain('TOTAL')
    expect(wrapper.text()).not.toContain('ABILITY TABLE')
    expect(wrapper.text()).not.toContain('rows')
    expect(wrapper.find('.panel-title').exists()).toBe(false)
  })

  it('filters rows from the search input', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    await wrapper.get('[data-testid="skills-search-input"]').setValue('Trend')

    expect(wrapper.find('[data-testid="skills-table-row-skill-schedule-1"]').exists()).toBe(false)
    expect(wrapper.get('[data-testid="skills-table-row-skill-analyst-1"]').text()).toContain('Trend Lens')
  })

  it('opens the detail drawer when a row is activated', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    await wrapper.get('[data-testid="skills-table-row-skill-analyst-1"]').trigger('click')

    expect(wrapper.get('[data-testid="skills-detail-panel"]').text()).toContain('Trend Lens')
    expect(wrapper.get('[data-testid="skills-detail-panel"]').text()).toContain('Summarizes movement signals.')
  })

  it('closes the detail drawer from the close button', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    await wrapper.get('[data-testid="skills-table-row-skill-analyst-1"]').trigger('click')
    await wrapper.get('[aria-label="关闭详情滑窗"]').trigger('click')

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

  it('keeps the selected drawer record stable when filters hide its row', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    await wrapper.get('[data-testid="skills-table-row-skill-analyst-1"]').trigger('click')
    await wrapper.get('[data-testid="skills-search-input"]').setValue('Priority')
    await nextTick()

    expect(wrapper.find('[data-testid="skills-table-row-skill-analyst-1"]').exists()).toBe(false)
    expect(wrapper.get('[data-testid="skills-detail-panel"]').text()).toContain('Trend Lens')
  })

  it('renders the detail drawer as an accessible dialog and closes on escape', async () => {
    const wrapper = mount(SkillsPage, { attachTo: document.body })
    await Promise.resolve()
    await Promise.resolve()

    await wrapper.get('[data-testid="skills-table-row-skill-analyst-1"]').trigger('click')
    await nextTick()

    const panel = wrapper.get('[data-testid="skills-detail-panel"]')
    expect(panel.attributes('role')).toBe('dialog')
    expect(panel.attributes('aria-modal')).toBe('true')

    await panel.trigger('keydown', { key: 'Escape' })
    await nextTick()

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

  it('opens and closes the drawer without animation when reduced motion is enabled', async () => {
    vi.stubGlobal('matchMedia', vi.fn().mockImplementation(() => ({
      matches: true,
      media: '(prefers-reduced-motion: reduce)',
      onchange: null,
      addListener: vi.fn(),
      removeListener: vi.fn(),
      addEventListener: vi.fn(),
      removeEventListener: vi.fn(),
      dispatchEvent: vi.fn(),
    })))

    const wrapper = mount(SkillsPage, { attachTo: document.body })
    await Promise.resolve()
    await Promise.resolve()

    await wrapper.get('[data-testid="skills-table-row-skill-analyst-1"]').trigger('click')
    await nextTick()
    expect(wrapper.find('[data-testid="skills-detail-panel"]').exists()).toBe(true)

    await wrapper.get('[aria-label="关闭详情滑窗"]').trigger('click')
    await nextTick()
    expect(wrapper.find('[data-testid="skills-detail-panel"]').exists()).toBe(false)

    wrapper.unmount()
  })

  it('bootstraps builtin skills when the first list is empty', async () => {
    mocks.list.mockResolvedValueOnce({ data: [] })
    mocks.bootstrapBuiltin.mockResolvedValueOnce({ data: skillFixtures })

    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()
    await Promise.resolve()

    expect(mocks.bootstrapBuiltin).toHaveBeenCalledTimes(1)
    expect(wrapper.text()).toContain('Priority Router')
    expect(wrapper.find('[data-testid="skills-overview"]').exists()).toBe(false)
  })

  it('opens the MCP panel inside the skills page when MCP is clicked', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    expect(wrapper.find('[data-testid="skills-mcp-panel"]').exists()).toBe(false)

    await wrapper.get('[data-testid="skills-mcp-button"]').trigger('click')

    expect(wrapper.get('[data-testid="skills-mcp-panel"]').text()).toContain('MCP 工具能力包')
    expect(wrapper.get('[data-testid="skills-page-title"]').text()).toContain('能力中心')
  })

  it('submits schedule_planner agent type when creating a skill', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    await wrapper.get('[data-testid="skills-create-button"]').trigger('click')
    await wrapper.get('[data-testid="skill-agent-type-select"]').setValue('schedule_planner')
    await wrapper.get('[data-testid="skill-name-input"]').setValue('Planner Skill')
    await wrapper.get('[data-testid="skill-instructions-input"]').setValue('Prioritize schedule risks.')
    await wrapper.get('[data-testid="skill-save-button"]').trigger('click')
    await Promise.resolve()

    expect(mocks.create).toHaveBeenCalledWith(expect.objectContaining({ agent_type: 'schedule_planner' }))
  })

  it('keeps the create skill modal working after the MCP action is added', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    await wrapper.get('[data-testid="skills-create-button"]').trigger('click')

    expect(wrapper.get('[data-testid="skills-skill-modal"]').text()).toContain('新建技能')
    expect(wrapper.find('[data-testid="skills-mcp-panel"]').exists()).toBe(false)
  })

  it('marks the table region as the internal scroll container', async () => {
    const wrapper = mount(SkillsPage)
    await Promise.resolve()
    await Promise.resolve()

    const tableWrap = wrapper.get('[data-testid="skills-table-wrap"]')
    expect(tableWrap.classes()).toContain('skills-table-wrap')
    expect(wrapper.find('.table-viewport').exists()).toBe(true)
  })
})
