import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import UsersView from '@/views/UsersView.vue'

vi.mock('@/api/client', () => ({
  get: vi.fn(),
  post: vi.fn(),
  patch: vi.fn(),
}))

vi.mock('@/composables/useToast', () => ({
  useToast: () => ({
    success: vi.fn(),
    error: vi.fn(),
    warning: vi.fn(),
    info: vi.fn(),
  }),
}))

vi.mock('@/components/AppHeader.vue', () => ({
  default: {
    props: ['title', 'description'],
    template: `
      <div data-testid="app-header" :title="title" :description="description">
        <slot name="actions" />
        <slot name="subtitle" />
      </div>
    `,
  },
}))

vi.mock('@/components/EmptyState.vue', () => ({
  default: {
    props: ['title', 'description'],
    template: '<div data-testid="empty-state">{{ title }}</div>',
  },
}))

vi.mock('@/components/SkeletonBlock.vue', () => ({
  default: { template: '<div data-testid="skeleton" />' },
}))

vi.mock('@/components/InlineError.vue', () => ({
  default: {
    props: ['title', 'message'],
    template: '<div data-testid="inline-error">{{ message }}</div>',
  },
}))

vi.mock('@/components/ConfirmDialog.vue', () => ({
  default: {
    props: ['open', 'title', 'confirmDisabled'],
    emits: ['confirm', 'cancel'],
    template: `
      <div v-if="open" data-testid="confirm-dialog">
        <slot />
        <button type="button" data-testid="confirm-ok" @click="$emit('confirm')">OK</button>
        <button type="button" data-testid="confirm-cancel" @click="$emit('cancel')">Cancel</button>
      </div>
    `,
  },
}))

import { get, post, patch } from '@/api/client'

const mockedGet = vi.mocked(get)
const mockedPost = vi.mocked(post)
const mockedPatch = vi.mocked(patch)

const seedUsers = [
  {
    id: 'u1',
    username: 'entry1',
    fullName: 'Entry Clerk',
    role: 'DATA_ENTRY_CLERK',
  },
  {
    id: 'u2',
    username: 'verifier1',
    fullName: 'Verifier One',
    role: 'VERIFIER',
  },
]

beforeEach(() => {
  setActivePinia(createPinia())
  vi.clearAllMocks()
  mockedGet.mockResolvedValue({
    success: true,
    statusCode: 200,
    data: seedUsers,
    error: null,
  })
})

describe('UsersView', () => {
  it('loads and lists users on mount', async () => {
    const wrapper = mount(UsersView)
    await flushPromises()

    expect(mockedGet).toHaveBeenCalledWith('users', undefined)
    expect(wrapper.text()).toContain('Entry Clerk')
    expect(wrapper.text()).toContain('Verifier One')
    expect(wrapper.find('[data-testid="user-row-entry1"]').exists()).toBe(true)
  })

  it('creates a user from the create form', async () => {
    mockedPost.mockResolvedValueOnce({
      success: true,
      statusCode: 201,
      data: {
        id: 'u3',
        username: 'newclerk',
        fullName: 'New Clerk',
        role: 'DATA_ENTRY_CLERK',
      },
      error: null,
    })

    const wrapper = mount(UsersView)
    await flushPromises()

    await wrapper.get('[data-testid="users-toggle-create"]').trigger('click')
    expect(wrapper.find('[data-testid="users-create-form"]').exists()).toBe(true)

    await wrapper.get('#create-username').setValue('newclerk')
    await wrapper.get('#create-fullname').setValue('New Clerk')
    await wrapper.get('#create-password').setValue('Password1')
    await wrapper.get('[data-testid="users-create-form"] form').trigger('submit')
    await flushPromises()

    expect(mockedPost).toHaveBeenCalledWith('users', {
      username: 'newclerk',
      fullName: 'New Clerk',
      role: 'DATA_ENTRY_CLERK',
      password: 'Password1',
    })
    expect(mockedGet).toHaveBeenCalledTimes(2)
  })

  it('edits a user full name and role', async () => {
    mockedPatch.mockResolvedValueOnce({
      success: true,
      statusCode: 200,
      data: {
        id: 'u1',
        username: 'entry1',
        fullName: 'Updated Clerk',
        role: 'VERIFIER',
      },
      error: null,
    })

    const wrapper = mount(UsersView)
    await flushPromises()

    const row = wrapper.get('[data-testid="user-row-entry1"]')
    await row.findAll('button').find((b) => b.text() === 'Edit')!.trigger('click')
    expect(wrapper.find('[data-testid="users-edit-form"]').exists()).toBe(true)

    await wrapper.get('#edit-fullname').setValue('Updated Clerk')
    await wrapper.get('#edit-role').setValue('VERIFIER')
    await wrapper.get('[data-testid="users-edit-form"] form').trigger('submit')
    await flushPromises()

    expect(mockedPatch).toHaveBeenCalledWith('users/u1', {
      fullName: 'Updated Clerk',
      role: 'VERIFIER',
    })
  })

  it('resets password via confirm dialog', async () => {
    mockedPost.mockResolvedValueOnce('' as never)

    const wrapper = mount(UsersView)
    await flushPromises()

    await wrapper.get('[data-testid="user-row-entry1"] [data-testid="users-reset-password"]').trigger('click')
    expect(wrapper.find('[data-testid="confirm-dialog"]').exists()).toBe(true)

    await wrapper.get('[data-testid="users-reset-password-input"]').setValue('Password9')
    await wrapper.get('[data-testid="confirm-ok"]').trigger('click')
    await flushPromises()

    expect(mockedPost).toHaveBeenCalledWith('users/u1/reset-password', {
      newPassword: 'Password9',
    })
  })

  it('deactivates a user', async () => {
    mockedPatch.mockResolvedValueOnce({
      success: true,
      statusCode: 200,
      data: seedUsers[0],
      error: null,
    })

    const wrapper = mount(UsersView)
    await flushPromises()

    await wrapper.get('[data-testid="user-row-entry1"] [data-testid="users-deactivate"]').trigger('click')
    await flushPromises()

    expect(mockedPatch).toHaveBeenCalledWith('users/u1', { isActive: false })
  })
})
