import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { nextTick } from 'vue'
import { message } from 'ant-design-vue'
import UserFormModal from '@/components/UserFormModal.vue'

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: (query: string) => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: () => {},
    removeListener: () => {},
    addEventListener: () => {},
    removeEventListener: () => {},
    dispatchEvent: () => false,
  }),
})

vi.mock('@/utils/request', () => ({
  api: {
    get: vi.fn(),
    post: vi.fn(),
    put: vi.fn(),
  },
}))

const mockUserData = {
  id: 1,
  username: 'testuser',
  email: 'test@test.com',
  status: 1,
  role_id: 1,
  ext: null,
  created_at: '2026-01-01T00:00:00Z',
  updated_at: '2026-01-01T00:00:00Z',
}

describe('UserFormModal', () => {
  let wrapper: ReturnType<typeof mount>

  beforeEach(() => {
    vi.restoreAllMocks()
    document.body.innerHTML = ''
  })

  afterEach(() => {
    wrapper?.unmount()
    document.body.innerHTML = ''
  })

  async function mountModal(props = {}) {
    wrapper = mount(UserFormModal, {
      props: {
        open: true,
        mode: 'create' as const,
        userData: null,
        ...props,
      },
      attachTo: document.body,
    })
    await flushPromises()
    await nextTick()
    return wrapper
  }

  // 查找 teleported 到 body 的模态框内容
  function queryBody(selector: string) {
    return document.body.querySelectorAll(selector)
  }

  describe('create mode', () => {
    it('renders create title', async () => {
      await mountModal()
      const title = document.body.querySelector('.ant-modal-title')
      expect(title?.textContent).toBe('新建用户')
    })

    it('shows password field in create mode', async () => {
      await mountModal()
      const labels = Array.from(queryBody('.ant-form-item label'))
        .map((el) => el.textContent?.trim())
      expect(labels).toContain('密码')
    })

    it('shows all five form fields', async () => {
      await mountModal()
      const formItems = queryBody('.ant-form-item')
      expect(formItems).toHaveLength(5) // username, password, email, status, role
    })

    it('emits update:open false on cancel', async () => {
      const w = await mountModal()
      // 找到取消按钮（模态框 footer 中非 primary 按钮）
      const buttons = Array.from(queryBody('.ant-modal-footer .ant-btn'))
      const cancelBtn = buttons.find((b) => !b.classList.contains('ant-btn-primary'))
      cancelBtn?.dispatchEvent(new Event('click'))
      await flushPromises()
      expect(w.emitted('update:open')?.[0]).toEqual([false])
    })
  })

  describe('edit mode', () => {
    it('renders edit title', async () => {
      await mountModal({ mode: 'edit', userData: mockUserData })
      const title = document.body.querySelector('.ant-modal-title')
      expect(title?.textContent).toBe('编辑用户')
    })

    it('hides password field in edit mode', async () => {
      await mountModal({ mode: 'edit', userData: mockUserData })
      const labels = Array.from(queryBody('.ant-form-item label'))
        .map((el) => el.textContent?.trim())
      expect(labels).not.toContain('密码')
    })

    it('shows four form fields (no password)', async () => {
      await mountModal({ mode: 'edit', userData: mockUserData })
      const formItems = queryBody('.ant-form-item')
      expect(formItems).toHaveLength(4) // username, email, status, role
    })

    it('prefills form with user data', async () => {
      // 先以 open=false 挂载，再切换为 true 触发 watch 预填数据（与实际使用流程一致）
      const w = await mountModal({ mode: 'edit', userData: mockUserData, open: false })
      await w.setProps({ open: true })
      await flushPromises()
      await nextTick()

      const inputs = Array.from(queryBody('input')) as HTMLInputElement[]
      const values = inputs.map((i) => i.value)
      expect(values).toContain('testuser')
      expect(values).toContain('test@test.com')
    })
  })

  describe('edge cases', () => {
    it('shows warning when role list fetch fails', async () => {
      const { api } = await import('@/utils/request')
      vi.mocked(api.get).mockRejectedValueOnce(new Error('Network error'))
      const warnSpy = vi.spyOn(message, 'warning').mockImplementation(() => ({}) as never)

      // open=false then switch to true triggers fetchRoles
      const w = await mountModal({ open: false })
      await w.setProps({ open: true })
      await flushPromises()
      await nextTick()

      expect(warnSpy).toHaveBeenCalledWith('获取角色列表失败，角色选择不可用')
      warnSpy.mockRestore()
    })

    it('shows warning when user created but role assignment fails', async () => {
      const { api } = await import('@/utils/request')
      // fetchRoles succeeds
      vi.mocked(api.get).mockResolvedValueOnce([{ id: 1, name: 'administrator' }])
      // user create succeeds
      vi.mocked(api.post).mockResolvedValueOnce({ id: 99 })
      // role assignment fails
      vi.mocked(api.put).mockRejectedValueOnce(new Error('Role assign failed'))

      const warnSpy = vi.spyOn(message, 'warning').mockImplementation(() => ({}) as never)
      const successSpy = vi.spyOn(message, 'success').mockImplementation(() => ({}) as never)

      const w = await mountModal({ open: false })
      await w.setProps({ open: true })
      await flushPromises()
      await nextTick()

      // Set role_id via component internals
      const vm = w.vm as unknown as { formState: { username: string; password: string; email: string; role_id: number | undefined } }
      vm.formState.username = 'newuser'
      vm.formState.password = 'pass123456'
      vm.formState.email = 'new@test.com'
      vm.formState.role_id = 1
      await nextTick()

      // Submit
      const okBtn = document.body.querySelector('.ant-modal-footer .ant-btn-primary') as HTMLElement
      okBtn?.click()
      await flushPromises()
      await nextTick()
      await flushPromises()

      // Role assignment warning shown but overall success emitted
      expect(warnSpy).toHaveBeenCalledWith('用户已保存，但角色分配失败，请稍后在用户列表中重试')
      expect(successSpy).toHaveBeenCalledWith('操作成功')
      expect(w.emitted('success')).toBeTruthy()

      warnSpy.mockRestore()
      successSpy.mockRestore()
    })
  })

  describe('form submission', () => {
    it('calls api.post for create mode', async () => {
      const { api } = await import('@/utils/request')
      vi.mocked(api.post).mockResolvedValueOnce({})

      await mountModal()

      // 填充表单
      const inputs = Array.from(queryBody('.ant-form-item input')) as HTMLInputElement[]
      expect(inputs.length).toBeGreaterThanOrEqual(3)
      inputs[0]!.value = 'newuser'
      inputs[0]!.dispatchEvent(new Event('input'))
      inputs[1]!.value = 'password123'
      inputs[1]!.dispatchEvent(new Event('input'))
      inputs[2]!.value = 'new@test.com'
      inputs[2]!.dispatchEvent(new Event('input'))
      await flushPromises()

      // 点击确定按钮
      const okBtn = document.body.querySelector('.ant-modal-footer .ant-btn-primary') as HTMLElement
      okBtn?.click()
      await flushPromises()
    })

    it('calls api.put for edit mode with status field', async () => {
      const { api } = await import('@/utils/request')
      vi.mocked(api.put).mockResolvedValueOnce({})

      // 先以 open=false 挂载，再切换为 true 触发 watch 预填数据，使表单验证通过
      const w = await mountModal({ mode: 'edit', userData: mockUserData, open: false })
      await w.setProps({ open: true })
      await flushPromises()
      await nextTick()

      // 点击确定按钮
      const okBtn = document.body.querySelector('.ant-modal-footer .ant-btn-primary') as HTMLElement
      expect(okBtn).toBeTruthy()
      okBtn?.click()
      await flushPromises()

      // 验证 PUT payload 包含 status 且不包含 ext
      expect(api.put).toHaveBeenCalledOnce()
      const [url, payload] = vi.mocked(api.put).mock.calls[0]!
      expect(url).toContain('/api/v1/users/')
      expect(payload).toHaveProperty('status')
      expect(payload).not.toHaveProperty('ext')
    })
  })
})
