import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { mount, flushPromises } from '@vue/test-utils'
import { nextTick } from 'vue'
import { message } from 'ant-design-vue'
import { ApiError } from '@/types/api'
import { useUserStore } from '@/stores/user'
import { fakeJwtWithRole } from '@/test/helpers'

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(),
    patch: vi.fn(),
    delete: vi.fn(),
  },
}))

vi.mock('vue-router', () => ({
  useRouter: () => ({ push: vi.fn() }),
  useRoute: () => ({ path: '/profile' }),
}))

function getSetupState(w: ReturnType<typeof mount>) {
  // @ts-expect-error setupState is Vue internal, not in public types
  return w.getCurrentComponent().setupState
}

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

  const mockUser = { id: 1, username: 'testuser', email: 'test@example.com', role: 'admin', status: 1 }

  beforeEach(() => {
    setActivePinia(createPinia())
    localStorage.clear()
    vi.restoreAllMocks()
  })

  afterEach(() => {
    wrapper?.unmount()
  })

  async function mountPage() {
    const { api } = await import('@/utils/request')
    vi.mocked(api.get).mockImplementation((url: string) => {
      if (url === '/api/v1/me/api-keys') return Promise.resolve([]) as never
      return Promise.resolve(mockUser) as never
    })

    const store = useUserStore()
    store.setToken(fakeJwtWithRole('administrator'), 'r.p.s', true)
    store.setUser({ ...mockUser })

    const { default: ProfilePage } = await import('../index.vue')
    wrapper = mount(ProfilePage, { attachTo: document.body })
    await flushPromises()
    await nextTick()
    return wrapper
  }

  it('renders user info in descriptions', async () => {
    await mountPage()
    const html = wrapper.html()

    expect(html).toContain('testuser')
    expect(html).toContain('admin')
    expect(html).toContain('1')
  })

  it('pre-fills email form field', async () => {
    await mountPage()
    const vm = getSetupState(wrapper)

    expect(vm.formState.email).toBe('test@example.com')
  })

  it('submits profile update successfully', async () => {
    const { api } = await import('@/utils/request')
    const updatedUser = { ...mockUser, email: 'new@example.com' }
    vi.mocked(api.put).mockResolvedValueOnce(updatedUser)
    const successSpy = vi.spyOn(message, 'success').mockImplementation(() => ({}) as never)

    await mountPage()
    const vm = getSetupState(wrapper)
    vm.formState.email = 'new@example.com'
    vm.formRef = { validate: vi.fn().mockResolvedValue(true) }
    await vm.handleSubmit()
    await flushPromises()

    expect(api.put).toHaveBeenCalledWith('/api/v1/me/profile', { email: 'new@example.com' })
    expect(successSpy).toHaveBeenCalledWith('个人信息更新成功')
    successSpy.mockRestore()
  })

  it('shows error message on ApiError', async () => {
    const { api } = await import('@/utils/request')
    vi.mocked(api.put).mockRejectedValueOnce(new ApiError('邮箱已被占用', 422))
    const errorSpy = vi.spyOn(message, 'error').mockImplementation(() => ({}) as never)

    await mountPage()
    const vm = getSetupState(wrapper)
    vm.formRef = { validate: vi.fn().mockResolvedValue(true) }
    await vm.handleSubmit()
    await flushPromises()

    expect(errorSpy).toHaveBeenCalledWith('邮箱已被占用')
    errorSpy.mockRestore()
  })

  it('shows network error on generic failure', async () => {
    const { api } = await import('@/utils/request')
    vi.mocked(api.put).mockRejectedValueOnce(new Error('Network failure'))
    const errorSpy = vi.spyOn(message, 'error').mockImplementation(() => ({}) as never)

    await mountPage()
    const vm = getSetupState(wrapper)
    vm.formRef = { validate: vi.fn().mockResolvedValue(true) }
    await vm.handleSubmit()
    await flushPromises()

    expect(errorSpy).toHaveBeenCalledWith('更新失败，请检查网络连接')
    errorSpy.mockRestore()
  })
})
