/**
 * VISUAL-01 / VISUAL-03 / VISUAL-04: Visual consistency invariants for AppSidebar
 *
 * Tests that:
 * 1. Skeleton items use Tailwind width classes, not data-driven inline styles (VISUAL-01)
 * 2. Nav-link buttons have focus-visible ring classes for keyboard accessibility (VISUAL-04)
 * 3. Expand/collapse toggle buttons have focus-visible ring classes (VISUAL-04)
 */
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { setActivePinia, createPinia } from 'pinia'
import { createRouter, createMemoryHistory } from 'vue-router'

vi.mock('../../../api/client.js', () => ({
  listFolders: vi.fn().mockResolvedValue({ items: [] }),
  getSharedWithMe: vi.fn().mockResolvedValue([]),
  listCloudConnections: vi.fn().mockResolvedValue({ items: [] }),
  listTopics: vi.fn().mockResolvedValue({ topics: [] }),
  getMyQuota: vi.fn().mockResolvedValue({ used_bytes: 0, limit_bytes: 104857600 }),
}))

const STUBS = {
  FolderTreeItem: true,
  CloudProviderTreeItem: true,
  QuotaBar: true,
  RouterLink: true,
  EmptyState: true,
}

function makeRouter() {
  return createRouter({
    history: createMemoryHistory(),
    routes: [
      { path: '/', component: { template: '<div/>' } },
      { path: '/settings', component: { template: '<div/>' } },
      { path: '/topics', component: { template: '<div/>' } },
      { path: '/shared', component: { template: '<div/>' } },
      { path: '/cloud', component: { template: '<div/>' } },
      { path: '/admin', component: { template: '<div/>' } },
    ],
  })
}

describe('VISUAL-01: Sidebar skeleton items use class-based widths (no inline style)', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.resetModules()
  })

  it('skeleton width divs have w-12/w-16/w-20 class, not style attribute', async () => {
    vi.doMock('../../../api/client.js', () => ({
      listFolders: vi.fn().mockResolvedValue({ items: [] }),
      getSharedWithMe: vi.fn().mockResolvedValue([]),
      listCloudConnections: vi.fn().mockReturnValue(new Promise(() => {})),
      listTopics: vi.fn().mockResolvedValue({ topics: [] }),
      getMyQuota: vi.fn().mockResolvedValue({ used_bytes: 0, limit_bytes: 104857600 }),
    }))

    setActivePinia(createPinia())
    const { useCloudConnectionsStore } = await import('../../../stores/cloudConnections.js')
    const cloudStore = useCloudConnectionsStore()
    cloudStore.loading = true
    cloudStore.connections = []

    const router = makeRouter()
    await router.push('/')
    await router.isReady()

    const AppSidebar = (await import('../AppSidebar.vue')).default
    const wrapper = mount(AppSidebar, {
      global: { plugins: [router], stubs: STUBS },
    })

    cloudStore.loading = true
    await wrapper.vm.$nextTick()

    // All skeleton width divs should use Tailwind class, not inline style width
    const skeletonWidthDivs = wrapper
      .findAll('.animate-pulse')
      .filter(el => {
        const cls = el.classes()
        return cls.includes('h-3') // width skeleton lines (not icon squares)
      })

    expect(skeletonWidthDivs.length).toBeGreaterThan(0)

    for (const el of skeletonWidthDivs) {
      // Must NOT have an inline style with width
      const style = el.attributes('style') || ''
      expect(style).not.toMatch(/width\s*:/)

      // Must have a Tailwind width class (w-12, w-16, or w-20)
      const cls = el.classes().join(' ')
      const hasWidthClass = /\bw-\d+\b/.test(cls)
      expect(hasWidthClass).toBe(true)
    }

    wrapper.unmount()
  })

  it('topics skeleton items use width classes, not inline style', async () => {
    vi.doMock('../../../api/client.js', () => ({
      listFolders: vi.fn().mockResolvedValue({ items: [] }),
      getSharedWithMe: vi.fn().mockResolvedValue([]),
      listCloudConnections: vi.fn().mockResolvedValue({ items: [] }),
      listTopics: vi.fn().mockResolvedValue({ topics: [] }),
      getMyQuota: vi.fn().mockResolvedValue({ used_bytes: 0, limit_bytes: 104857600 }),
    }))

    setActivePinia(createPinia())
    const { useTopicsStore } = await import('../../../stores/topics.js')
    const topicsStore = useTopicsStore()
    topicsStore.loading = true
    topicsStore.topics = []

    const router = makeRouter()
    await router.push('/')
    await router.isReady()

    const AppSidebar = (await import('../AppSidebar.vue')).default
    const wrapper = mount(AppSidebar, {
      global: { plugins: [router], stubs: STUBS },
    })

    topicsStore.loading = true
    await wrapper.vm.$nextTick()

    const skeletonWidthDivs = wrapper
      .findAll('.animate-pulse')
      .filter(el => el.classes().includes('h-3'))

    expect(skeletonWidthDivs.length).toBeGreaterThan(0)

    for (const el of skeletonWidthDivs) {
      const style = el.attributes('style') || ''
      expect(style).not.toMatch(/width\s*:/)
    }

    wrapper.unmount()
  })
})

describe('VISUAL-04: Sidebar interactive elements have focus-visible ring classes', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.resetModules()
  })

  it('expand/collapse folder toggle button has focus-visible:ring-2', async () => {
    vi.doMock('../../../api/client.js', () => ({
      listFolders: vi.fn().mockResolvedValue({ items: [] }),
      getSharedWithMe: vi.fn().mockResolvedValue([]),
      listCloudConnections: vi.fn().mockResolvedValue({ items: [] }),
      listTopics: vi.fn().mockResolvedValue({ topics: [] }),
      getMyQuota: vi.fn().mockResolvedValue({ used_bytes: 0, limit_bytes: 104857600 }),
    }))

    setActivePinia(createPinia())
    const router = makeRouter()
    await router.push('/')
    await router.isReady()

    const AppSidebar = (await import('../AppSidebar.vue')).default
    const wrapper = mount(AppSidebar, {
      global: { plugins: [router], stubs: STUBS },
    })
    await flushPromises()

    const toggleBtn = wrapper.find('button[title="Expand folders"]')
    expect(toggleBtn.exists()).toBe(true)
    const cls = toggleBtn.attributes('class') || ''
    expect(cls).toContain('focus-visible:ring-2')
    expect(cls).toContain('focus-visible:ring-indigo-500')

    wrapper.unmount()
  })

  it('sign-out button has focus-visible:ring-2', async () => {
    vi.doMock('../../../api/client.js', () => ({
      listFolders: vi.fn().mockResolvedValue({ items: [] }),
      getSharedWithMe: vi.fn().mockResolvedValue([]),
      listCloudConnections: vi.fn().mockResolvedValue({ items: [] }),
      listTopics: vi.fn().mockResolvedValue({ topics: [] }),
      getMyQuota: vi.fn().mockResolvedValue({ used_bytes: 0, limit_bytes: 104857600 }),
    }))

    setActivePinia(createPinia())
    const { useAuthStore } = await import('../../../stores/auth.js')
    const authStore = useAuthStore()
    authStore.user = { email: 'test@example.com', role: 'user' }

    const router = makeRouter()
    await router.push('/')
    await router.isReady()

    const AppSidebar = (await import('../AppSidebar.vue')).default
    const wrapper = mount(AppSidebar, {
      global: { plugins: [router], stubs: STUBS },
    })
    await flushPromises()

    const signOutBtn = wrapper.find('button[aria-label="Sign out"]')
    expect(signOutBtn.exists()).toBe(true)
    const cls = signOutBtn.attributes('class') || ''
    expect(cls).toContain('focus-visible:ring-2')

    wrapper.unmount()
  })
})
