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/>' } },
    ],
  })
}

async function mountSidebarWithStubs(apiOverrides = {}) {
  const clientModule = await import('../../../api/client.js')
  Object.assign(clientModule, apiOverrides)

  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()
  return wrapper
}

describe('UX-03: AppSidebar shows skeleton placeholders while loading', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.resetModules()
  })

  it('renders skeleton rows in cloud section when loadingCloudConnections=true', 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
    cloudStore.connections = []
    await wrapper.vm.$nextTick()

    const pulses = wrapper.findAll('.animate-pulse')
    expect(pulses.length).toBeGreaterThanOrEqual(3)
    wrapper.unmount()
  })

  it('renders skeleton rows in topics section when topicsStore.loading=true', 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
    topicsStore.topics = []
    await wrapper.vm.$nextTick()

    const pulses = wrapper.findAll('.animate-pulse')
    expect(pulses.length).toBeGreaterThanOrEqual(3)
    wrapper.unmount()
  })

  it('Loading… text is removed from all three sections', 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()

    expect(wrapper.html()).not.toContain('Loading…')
    wrapper.unmount()
  })
})

describe('UX-01 (sidebar micro): EmptyState size=sm appears when each section is empty', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.resetModules()
  })

  it('folders empty: EmptyState size=sm with icon=folder', 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 { useFoldersStore } = await import('../../../stores/folders.js')
    const foldersStore = useFoldersStore()

    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()

    foldersStore.rootFolders = []
    await wrapper.vm.$nextTick()

    await wrapper.find('button[title="Expand folders"]').trigger('click')
    await wrapper.vm.$nextTick()

    const emptyStateStub = wrapper.find('empty-state-stub')
    expect(emptyStateStub.exists()).toBe(true)
    expect(emptyStateStub.attributes('icon')).toBe('folder')
    expect(emptyStateStub.attributes('size')).toBe('sm')
    wrapper.unmount()
  })

  it('topics empty: EmptyState size=sm with icon=tag', 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()

    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()

    topicsStore.topics = []
    topicsStore.loading = false
    await wrapper.vm.$nextTick()

    const emptyStates = wrapper.findAll('empty-state-stub')
    const tagEmpty = emptyStates.find(e => e.attributes('icon') === 'tag')
    expect(tagEmpty).toBeDefined()
    expect(tagEmpty.attributes('size')).toBe('sm')
    wrapper.unmount()
  })

  it('cloud empty: EmptyState size=sm with icon=cloud and a Settings link in #cta', 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 { useCloudConnectionsStore } = await import('../../../stores/cloudConnections.js')
    const cloudStore = useCloudConnectionsStore()

    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, EmptyState: false } },
    })
    await flushPromises()

    cloudStore.connections = []
    cloudStore.loading = false
    await wrapper.vm.$nextTick()

    const html = wrapper.html()
    expect(html).toContain('Connect in Settings')
    wrapper.unmount()
  })
})

describe('UX-14: AppSidebar no longer renders an inline "New" folder button', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.resetModules()
  })

  it('no <button> with text "New" exists in the rendered template', 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 buttons = wrapper.findAll('button')
    const newButtons = buttons.filter(b => b.text().trim() === 'New')
    expect(newButtons.length).toBe(0)
    wrapper.unmount()
  })

  it('startNewFolder/cancelNewFolder/submitNewFolder methods are removed', 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()

    expect(wrapper.vm.startNewFolder).toBeUndefined()
    expect(wrapper.vm.cancelNewFolder).toBeUndefined()
    expect(wrapper.vm.submitNewFolder).toBeUndefined()
    wrapper.unmount()
  })

  it('newFolderName/showNewFolderInput state is removed (no inline folder input in DOM)', 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 inputs = wrapper.findAll('input[placeholder="Folder name"]')
    expect(inputs.length).toBe(0)
    wrapper.unmount()
  })
})
