import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from '@jest/globals'
import mockfs from 'mock-fs'
import request from 'supertest'
import { createApp } from '../src/createApp'
import { createMockServer, getRandomPort } from './util.js'

describe('Manifest service', () => {
  let app
  let mockserver
  const port = getRandomPort()

  beforeAll(() => {
    mockfs({
      './config/manifests': {
        'urls.yaml': `manifests:
          - http://localhost:${port}/api/manifest.json`
      }
    })
    app = createApp()
  })

  afterAll(() => {
    mockfs.restore()
  })

  beforeEach(async () => {
    mockserver = await createMockServer({ port })
    mockserver.respondWith({
      '/api/manifest.json': { some: 'thing' }
    })
  })

  afterEach(() => {
    mockserver.close()
    app.timeout = 30000
  })

  it('is healthy', async () => {
    const response = await request(app).get('/healthy')
    expect(response.statusCode).toBe(200)
  })

  it('fetches manifest data', async () => {
    const response = await request(app).get('/api/manifest.json')
    expect(response.statusCode).toBe(200)
    expect(response.body).toEqual([{ some: 'thing' }])
  })

  it('caches manifest data', async () => {
    await request(app)
      .get('/api/manifest.json')
      .then(response => {
        expect(response.statusCode).toBe(200)
        expect(response.body).toEqual([{ some: 'thing' }])
      })

    mockserver.close()
    mockserver = await createMockServer({ port })
    mockserver.respondWith({ '/api/manifest.json': { some: 'different' } })

    await new Promise((resolve, reject) => setTimeout(resolve, 150))

    await request(app)
      .get('/api/manifest.json')
      .then(response => {
        expect(response.statusCode).toBe(200)
        expect(response.body).toEqual([{ some: 'thing' }])
      })
  })

  it('refreshes manifest data after caching timeout', async () => {
    app.timeout = 100

    await request(app)
      .get('/api/manifest.json')
      .then(response => {
        expect(response.statusCode).toBe(200)
        expect(response.body).toEqual([{ some: 'thing' }])
      })

    mockserver.close()
    mockserver = await createMockServer({ port })
    mockserver.respondWith({ '/api/manifest.json': { some: 'different' } })

    await new Promise((resolve, reject) => setTimeout(resolve, 150))

    await request(app)
      .get('/api/manifest.json')
      .then(response => {
        expect(response.statusCode).toBe(200)
        expect(response.body).toEqual([{ some: 'different' }])
      })
  })

  it.skip('can load multiple configurations', async () => {
    mockfs({
      './config/manifests': {
        'urls.yaml': `manifests:
          - https://some-fake-url.k3s.de/api/manifest.json
          - https://some-other-fake-url.k3s.de/api/manifest.json`
      }
    })

    mockserver.close()
    mockserver = await createMockServer({ port })
    mockserver.respondWith({ '/api/manifest.json': { some: 'other' } })

    const app = createApp()

    await request(app)
      .get('/api/manifest.json')
      .then(response => {
        expect(response.statusCode).toBe(200)
        expect(response.body).toEqual([{ some: 'thing"}' }, { some: 'other"}' }])
      })
  })
})