import { beforeEach, describe, expect, it, vi } from 'vitest'

const getMock = vi.fn()
const postMock = vi.fn()
const putMock = vi.fn()
const delMock = vi.fn()
const downloadMock = vi.fn()

vi.mock('@/lib/http/client', () => ({
  get: getMock,
  post: postMock,
  put: putMock,
  del: delMock,
  download: downloadMock,
}))

describe('additional service adapters', () => {
  beforeEach(() => {
    getMock.mockReset()
    postMock.mockReset()
    putMock.mockReset()
    delMock.mockReset()
    downloadMock.mockReset()
  })

  it('routes the remaining users service methods through the HTTP client', async () => {
    const {
      listUsers,
      getUser,
      updateUser,
      deleteUser,
      updateUserStatus,
      getUserRoles,
      assignUserRoles,
    } = await import('./users')

    await listUsers({ page: 2, page_size: 50, keyword: 'alice', status: 1, role_ids: '3' })
    expect(getMock).toHaveBeenCalledWith('/users', {
      page: 2,
      page_size: 50,
      keyword: 'alice',
      status: 1,
      role_ids: '3',
    })

    await getUser(7)
    expect(getMock).toHaveBeenCalledWith('/users/7')

    await updateUser(7, { nickname: 'Alice' })
    expect(putMock).toHaveBeenCalledWith('/users/7', { nickname: 'Alice' })

    await deleteUser(7)
    expect(delMock).toHaveBeenCalledWith('/users/7')

    await updateUserStatus(7, { status: 3 })
    expect(putMock).toHaveBeenCalledWith('/users/7/status', { status: 3 })

    await getUserRoles(7)
    expect(getMock).toHaveBeenCalledWith('/users/7/roles')

    await assignUserRoles(7, { role_ids: [1, 2] })
    expect(putMock).toHaveBeenCalledWith('/users/7/roles', { role_ids: [1, 2] })
  })

  it('covers role, permission, device, and stats service endpoints', async () => {
    getMock
      .mockResolvedValueOnce({ items: [], total: 0, page: 1, page_size: 20 })
      .mockResolvedValueOnce({ id: 5, name: '管理员' })
      .mockResolvedValueOnce([{ id: 9 }, { id: 11 }])
      .mockResolvedValueOnce({ items: [], total: 0, page: 1, page_size: 20 })
      .mockResolvedValueOnce({ id: 3 })
      .mockResolvedValueOnce([{ id: 1, name: 'menu:view' }])
      .mockResolvedValueOnce([{ id: 2, name: 'menu:edit' }])
      .mockResolvedValueOnce({ total_users: 10 })
      .mockResolvedValueOnce({ active_users: 8 })

    const {
      listRoles,
      getRole,
      createRole,
      updateRole,
      deleteRole,
      updateRoleStatus,
      getRolePermissions,
      assignRolePermissions,
    } = await import('./roles')
    const {
      listDevices,
      getDevice,
      deleteDevice,
      updateDeviceStatus,
    } = await import('./devices')
    const {
      getPermissionTree,
      listPermissions,
      getPermission,
      createPermission,
      updatePermission,
      deletePermission,
      updatePermissionStatus,
    } = await import('./permissions')
    const { getDashboardStats, getUserStats } = await import('./stats')

    await listRoles({ page: 1, page_size: 20 })
    expect(getMock).toHaveBeenCalledWith('/roles', { page: 1, page_size: 20 })

    await getRole(5)
    expect(getMock).toHaveBeenCalledWith('/roles/5')

    await createRole({ name: '审计员', code: 'auditor' })
    expect(postMock).toHaveBeenCalledWith('/roles', { name: '审计员', code: 'auditor' })

    await updateRole(5, { description: 'updated' })
    expect(putMock).toHaveBeenCalledWith('/roles/5', { description: 'updated' })

    await deleteRole(5)
    expect(delMock).toHaveBeenCalledWith('/roles/5')

    await updateRoleStatus(5, 0)
    expect(putMock).toHaveBeenCalledWith('/roles/5/status', { status: 0 })

    const permissionIds = await getRolePermissions(5)
    expect(getMock).toHaveBeenCalledWith('/roles/5/permissions')
    expect(permissionIds).toEqual([9, 11])

    await assignRolePermissions(5, [9, 11])
    expect(putMock).toHaveBeenCalledWith('/roles/5/permissions', { permission_ids: [9, 11] })

    await listDevices({ page: 1, page_size: 10 })
    expect(getMock).toHaveBeenCalledWith('/devices', { page: 1, page_size: 10 })

    await getDevice(3)
    expect(getMock).toHaveBeenCalledWith('/devices/3')

    await deleteDevice(3)
    expect(delMock).toHaveBeenCalledWith('/devices/3')

    await updateDeviceStatus(3, 1)
    expect(putMock).toHaveBeenCalledWith('/devices/3/status', { status: 1 })

    await getPermissionTree()
    expect(getMock).toHaveBeenCalledWith('/permissions/tree')

    await listPermissions()
    expect(getMock).toHaveBeenCalledWith('/permissions')

    await getPermission(6)
    expect(getMock).toHaveBeenCalledWith('/permissions/6')

    await createPermission({ name: 'view dashboard', code: 'dashboard:view', type: 'menu' })
    expect(postMock).toHaveBeenCalledWith('/permissions', {
      name: 'view dashboard',
      code: 'dashboard:view',
      type: 'menu',
    })

    await updatePermission(6, { name: 'updated permission' })
    expect(putMock).toHaveBeenCalledWith('/permissions/6', { name: 'updated permission' })

    await deletePermission(6)
    expect(delMock).toHaveBeenCalledWith('/permissions/6')

    await updatePermissionStatus(6, 0)
    expect(putMock).toHaveBeenCalledWith('/permissions/6/status', { status: 0 })

    await getDashboardStats()
    expect(getMock).toHaveBeenCalledWith('/admin/stats/dashboard')

    await getUserStats()
    expect(getMock).toHaveBeenCalledWith('/admin/stats/users')
  })

  it('normalizes profile and log service responses and submits profile mutations', async () => {
    const file = new File(['avatar'], 'avatar.png', { type: 'image/png' })

    getMock
      .mockResolvedValueOnce({ id: 1, username: 'admin' })
      .mockResolvedValueOnce([{ id: 2, name: '管理员' }])
      .mockResolvedValueOnce({ totp_enabled: true })
      .mockResolvedValueOnce({ secret: 'demo-secret', qr_code_base64: 'abc', recovery_codes: ['r1'] })
      .mockResolvedValueOnce({
        list: [{ id: 1, status: 1, login_type: 1 }],
        total: 1,
        page: 2,
        size: 30,
      })
      .mockResolvedValueOnce({
        list: [{ id: 2, status: 0, login_type: 3 }],
        total: 1,
        page: 1,
        size: 5,
      })
      .mockResolvedValueOnce({
        list: [{ id: 3, operation_name: 'create user' }],
        total: 1,
        page: 3,
        size: 40,
      })
      .mockResolvedValueOnce({
        list: [{ id: 4, operation_name: 'update profile' }],
        total: 1,
        page: 1,
        size: 5,
      })

    const {
      getCurrentProfile,
      updateProfile,
      uploadAvatar,
      updatePassword,
      getTOTPStatus,
      getTOTPSetup,
      enableTOTP,
      disableTOTP,
    } = await import('./profile')
    const { listLoginLogs, listMyLoginLogs } = await import('./login-logs')
    const { listOperationLogs, listMyOperationLogs } = await import('./operation-logs')

    const currentProfile = await getCurrentProfile(1)
    expect(currentProfile).toEqual({
      user: { id: 1, username: 'admin' },
      roles: [{ id: 2, name: '管理员' }],
    })
    expect(getMock).toHaveBeenNthCalledWith(1, '/users/1')
    expect(getMock).toHaveBeenNthCalledWith(2, '/users/1/roles')

    await updateProfile(1, { nickname: 'Admin User' })
    expect(putMock).toHaveBeenCalledWith('/users/1', { nickname: 'Admin User' })

    await uploadAvatar(1, file)
    expect(postMock).toHaveBeenCalledWith('/users/1/avatar', expect.any(FormData))
    const avatarPayload = postMock.mock.calls[0][1] as FormData
    expect(avatarPayload.get('avatar')).toBe(file)

    await updatePassword(1, {
      current_password: 'CurrentPass123',
      new_password: 'NewPass123',
      confirm_password: 'NewPass123',
    })
    expect(putMock).toHaveBeenCalledWith('/users/1/password', {
      current_password: 'CurrentPass123',
      new_password: 'NewPass123',
      confirm_password: 'NewPass123',
    })

    await expect(getTOTPStatus()).resolves.toEqual({ totp_enabled: true })
    expect(getMock).toHaveBeenCalledWith('/auth/2fa/status')

    await expect(getTOTPSetup()).resolves.toEqual({
      secret: 'demo-secret',
      qr_code_base64: 'abc',
      recovery_codes: ['r1'],
    })
    expect(getMock).toHaveBeenCalledWith('/auth/2fa/setup')

    await enableTOTP('123456')
    expect(postMock).toHaveBeenCalledWith('/auth/2fa/enable', { code: '123456' })

    await disableTOTP('654321')
    expect(postMock).toHaveBeenCalledWith('/auth/2fa/disable', { code: '654321' })

    await expect(listLoginLogs({ page: 2, page_size: 30, status: 1 })).resolves.toEqual({
      items: [{ id: 1, status: 1, login_type: 1 }],
      total: 1,
      page: 2,
      page_size: 30,
    })
    expect(getMock).toHaveBeenCalledWith('/logs/login', { page: 2, page_size: 30, status: 1 })

    await expect(listMyLoginLogs({ page: 1, page_size: 5 })).resolves.toEqual({
      items: [{ id: 2, status: 0, login_type: 3 }],
      total: 1,
      page: 1,
      page_size: 5,
    })
    expect(getMock).toHaveBeenCalledWith('/logs/login/me', { page: 1, page_size: 5 })

    await expect(listOperationLogs({ page: 3, page_size: 40, method: 'POST' })).resolves.toEqual({
      items: [{ id: 3, operation_name: 'create user' }],
      total: 1,
      page: 3,
      page_size: 40,
    })
    expect(getMock).toHaveBeenCalledWith('/logs/operation', { page: 3, page_size: 40, method: 'POST' })

    await expect(listMyOperationLogs({ page: 1, page_size: 5 })).resolves.toEqual({
      items: [{ id: 4, operation_name: 'update profile' }],
      total: 1,
      page: 1,
      page_size: 5,
    })
    expect(getMock).toHaveBeenCalledWith('/logs/operation/me', { page: 1, page_size: 5 })
  })

  it('covers import-export download adapters and upload form submission', async () => {
    const blob = new Blob(['csv,data'], { type: 'text/csv' })
    const file = new File(['username'], 'users.csv', { type: 'text/csv' })
    const clickMock = vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => undefined)
    const createObjectURLMock = vi.fn(() => 'blob:mock')
    const revokeObjectURLMock = vi.fn()

    downloadMock.mockResolvedValue(blob)
    postMock.mockResolvedValue({
      success_count: 1,
      fail_count: 0,
      errors: [],
      message: 'ok',
    })

    Object.defineProperty(window.URL, 'createObjectURL', {
      configurable: true,
      value: createObjectURLMock,
    })
    Object.defineProperty(window.URL, 'revokeObjectURL', {
      configurable: true,
      value: revokeObjectURLMock,
    })

    const { exportUsers, downloadImportTemplate, importUsers } = await import('./import-export')

    await exportUsers({
      format: 'csv',
      fields: ['id', 'username'],
      keyword: 'alice',
      status: 1,
    })
    expect(downloadMock).toHaveBeenCalledWith('/admin/users/export', {
      format: 'csv',
      fields: 'id,username',
      keyword: 'alice',
      status: 1,
    })

    await downloadImportTemplate('xlsx')
    expect(downloadMock).toHaveBeenCalledWith('/admin/users/import/template', { format: 'xlsx' })

    await importUsers(file)
    expect(postMock).toHaveBeenCalledWith('/admin/users/import', expect.any(FormData))
    const importPayload = postMock.mock.calls[0][1] as FormData
    expect(importPayload.get('file')).toBe(file)

    expect(createObjectURLMock).toHaveBeenCalledTimes(2)
    expect(revokeObjectURLMock).toHaveBeenCalledTimes(2)
    expect(clickMock).toHaveBeenCalledTimes(2)
  })
})
