import { MockAgent, fetch as _fetch } from 'undici'
import { describe, expect, it } from 'vitest'
import { ModerinthApiError, ModrinthV2Client } from './index'

describe('ModrinthV2Client', () => {
  const agent = new MockAgent()
  agent.disableNetConnect()
  const fetch: typeof globalThis.fetch = (input, init) => {
    init = Object.assign(init || {}, {
      dispatcher: agent,
    })
    return _fetch(input as any, init as any) as any
  }

  describe('#searchProjects', () => {
    it('should be able to search projects with defaults', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/search?query=&filter=&index=downloads&offset=0&limit=10',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.searchProjects({ }))
        .resolves.toEqual({ data: 'hello' })
    })
    it('should be able to search projects with options', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/search?query=123&filter=44&index=downloads&offset=19&limit=100&facets=%5B%5B%22categories%3Aforge%22%5D%2C%5B%22versions%3A1.17.1%22%5D%2C%5B%22project_type%3Amod%22%5D%5D',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.searchProjects({ index: 'downloads', limit: 100, offset: 19, query: '123', filter: '44', facets: '[["categories:forge"],["versions:1.17.1"],["project_type:mod"]]' }))
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if search failed', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/search?query=&filter=&index=downloads&offset=0&limit=10',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.searchProjects({ }))
        .rejects.toThrow(ModerinthApiError)
    })
  })
  describe('#getProject', () => {
    it('should be able to get project', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/project/123',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProject('123'))
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if project not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/project/123',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProject('123'))
        .rejects.toThrow(ModerinthApiError)
    })
  })
  describe('#getProjectVersions', () => {
    it('should be able to get project versions', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/project/123/version',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectVersions('123'))
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if project not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/project/123/version',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectVersions('123'))
        .rejects.toThrow(ModerinthApiError)
    })
    it('should be able to get project versions with options', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/project/123/version?loaders=%5B%22forge%22%5D&game_versions=%5B%221.22%22%5D&featured=true',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectVersions('123', { loaders: ['forge'], gameVersions: ['1.22'], featured: true }))
        .resolves.toEqual({ data: 'hello' })
    })
  })
  describe('#getProjectVersion', () => {
    it('should be able to get project version', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/version/123',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectVersion('123'))
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if project not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/version/123',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectVersion('123'))
        .rejects.toThrow(ModerinthApiError)
    })
  })
  describe('#getProjectVersionsById', () => {
    it('should be able to get project versions by id', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/versions?ids=%5B%22123%22%2C%22345%22%5D',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectVersionsById(['123', '345']))
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if project not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/versions?ids=%5B%22123%22%2C%22345%22%5D',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectVersionsById(['123', '345']))
        .rejects.toThrow(ModerinthApiError)
    })
  })
  describe('#getProjectVersionsByHash', () => {
    it('should be able to get project versions by hash', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        method: 'POST',
        path: '/v2/version_files',
        body: JSON.stringify({ hashes: ['123', '345'], algorithm: 'sha1' }),
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectVersionsByHash(['123', '345']))
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if project not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        method: 'POST',
        path: '/v2/version_files',
        body: JSON.stringify({ hashes: ['123', '345'], algorithm: 'sha1' }),
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectVersionsByHash(['123', '345']))
        .rejects.toThrow(ModerinthApiError)
    })
    it('should be able to set algorithm', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        method: 'POST',
        path: '/v2/version_files',
        body: JSON.stringify({ hashes: ['123', '345'], algorithm: 'sha256' }),
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectVersionsByHash(['123', '345'], 'sha256'))
        .resolves.toEqual({ data: 'hello' })
    })
  })
  describe('#getLatestProjectVersion', () => {
    it('should be able to get latest project version', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        method: 'POST',
        path: '/v2/version_file/123/update?algorithm=sha1',
        body: JSON.stringify({ loaders: [], game_versions: [] }),
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getLatestProjectVersion('123'))
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if project not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        method: 'POST',
        path: '/v2/version_file/123/update?algorithm=sha1',
        body: JSON.stringify({ loaders: [], game_versions: [] }),
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getLatestProjectVersion('123'))
        .rejects.toThrow(ModerinthApiError)
    })
    it('should be able to set options', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        method: 'POST',
        path: '/v2/version_file/123/update?algorithm=sha1',
        body: JSON.stringify({ loaders: ['forge'], game_versions: ['1.22'] }),
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getLatestProjectVersion('123', { loaders: ['forge'], gameVersions: ['1.22'] }))
        .resolves.toEqual({ data: 'hello' })
    })
  })
  describe('#getLicenseTags', () => {
    it('should be able to get license tags', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/tag/license',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getLicenseTags())
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if project not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/tag/license',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getLicenseTags())
        .rejects.toThrow(ModerinthApiError)
    })
  })
  describe('#getCategoryTags', () => {
    it('should be able to get category tags', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/tag/category',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getCategoryTags())
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if project not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/tag/category',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getCategoryTags())
        .rejects.toThrow(ModerinthApiError)
    })
  })
  describe('#getGameVersionTags', () => {
    it('should be able to get game version tags', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/tag/game_version',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getGameVersionTags())
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if project not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/tag/game_version',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getGameVersionTags())
        .rejects.toThrow(ModerinthApiError)
    })
  })
  describe('#getLoaderTags', () => {
    it('should be able to get loader tags', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/tag/loader',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getLoaderTags())
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if project not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/tag/loader',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getLoaderTags())
        .rejects.toThrow(ModerinthApiError)
    })
  })
  describe('#getProjectTeamMembers', () => {
    it('should be able to get project team members', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/project/123/members',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectTeamMembers('123'))
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if project not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/project/123/members',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getProjectTeamMembers('123'))
        .rejects.toThrow(ModerinthApiError)
    })
  })
  describe('#getUser', () => {
    it('should be able to get user', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/user/123',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getUser('123'))
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if user not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/user/123',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getUser('123'))
        .rejects.toThrow(ModerinthApiError)
    })
  })
  describe('#getUserProjects', () => {
    it('should be able to get user projects', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/user/123/projects',
      }).reply(200, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getUserProjects('123'))
        .resolves.toEqual({ data: 'hello' })
    })
    it('should throw error if user not found', async () => {
      const mockPool = agent.get('https://api.modrinth.com')
      mockPool.intercept({
        path: '/v2/user/123/projects',
      }).reply(404, { data: 'hello' })
      const client = new ModrinthV2Client({ fetch })
      await expect(client.getUserProjects('123'))
        .rejects.toThrow(ModerinthApiError)
    })
  })
})
