import { Eltro as t, assert, stub } from 'eltro'
import GitProvider from '../../core/providers/git.mjs'

t.describe('#getLatestVersion()', function() {
  t.test('should return correct name and link in result', async function() {
    const assertName = 'Karen'
    const assertLink = 'Over The Future'
    const assertFilename = 'test-sc.7z'
    let stubber = stub()
    let provider = new GitProvider({}, stubber)

    stubber.resolves({ body: [
      { name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
    ]})

    let version = await provider.getLatestVersion()
    assert.strictEqual(version.version, assertName)
    assert.strictEqual(version.link, assertLink)
    assert.strictEqual(version.filename, assertFilename)
    assert.strictEqual(version.log, '')
  })

  t.test('should auto replace spaces with underscores result', async function() {
    const assertOriginalName = 'The Smell Of Sea'
    const assertCorrectName = 'The_Smell_Of_Sea'
    const assertLink = 'Over The Future'
    const assertFilename = 'test-sc.7z'
    let stubber = stub()
    let provider = new GitProvider({}, stubber)

    stubber.resolves({ body: [
      { name: assertOriginalName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
    ]})

    let version = await provider.getLatestVersion()
    assert.strictEqual(version.version, assertCorrectName)
    assert.strictEqual(version.link, assertLink)
    assert.strictEqual(version.filename, assertFilename)
    assert.strictEqual(version.log, '')
  })

  t.test('should skip zip files for now', async function() {
    const assertName = 'Karen'
    const assertLink = 'My Wings'
    const assertFilename = 'test-sc.zip'
    let stubber = stub()
    let provider = new GitProvider({}, stubber)

    stubber.resolves({ body: [
      { name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
    ]})

    let err = await assert.isRejected(provider.getLatestVersion())
    assert.match(err.message, /release/)
    assert.match(err.message, /found/)
  })

  t.test('should skip versions with no assets', async function() {
    const assertName = 'name1'
    const assertLink = 'somelink'
    const assertFilename = 'something-sc.7z'
    let stubber = stub()
    let provider = new GitProvider({}, stubber)

    stubber.resolves({ body: [
      { name: 'test', assets: [] },
      { name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
    ]})

    let version = await provider.getLatestVersion()
    assert.strictEqual(version.version, assertName)
    assert.strictEqual(version.link, assertLink)
    assert.strictEqual(version.filename, assertFilename)
  })

  t.test('should skip versions with non-sc filename', async function() {
    const assertName = 'name2'
    const assertLink = 'somelink2'
    const assertFilename = 'something-sc.7z'
    let stubber = stub()
    let provider = new GitProvider({}, stubber)

    stubber.resolves({ body: [
      { name: 'test', assets: [{ name: 'nope.7z', browser_download_url: 'nope' }] },
      { name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
    ]})

    let version = await provider.getLatestVersion()
    assert.strictEqual(version.version, assertName)
    assert.strictEqual(version.link, assertLink)
    assert.strictEqual(version.filename, assertFilename)
  })

  t.test('should skip assets with non-sc filename', async function() {
    const assertName = 'name3'
    const assertLink = 'somelink3'
    const assertFilename = 'something-sc.7z'
    let stubber = stub()
    let provider = new GitProvider({}, stubber)

    stubber.resolves({ body: [
      { name: 'test', assets: [{ name: 'nope.7z', browser_download_url: 'nope' }] },
      { name: assertName, assets: [
        { name: 'nope.7z', browser_download_url: 'nope' },
        { name: assertFilename, browser_download_url: assertLink },
      ] },
    ]})

    let version = await provider.getLatestVersion()
    assert.strictEqual(version.version, assertName)
    assert.strictEqual(version.link, assertLink)
    assert.strictEqual(version.filename, assertFilename)
  })

  t.test('should otherwise reject', async function() {
    let stubber = stub()
    let provider = new GitProvider({}, stubber)

    stubber.resolves({ body: [
      { name: 'test', assets: [{ name: 'nope.7z', browser_download_url: 'nope' }] },
      { name: 'test2', assets: [{ name: 'nope2.7z', browser_download_url: 'nope' },] },
      { name: 'test3', assets: [{ name: 'nope2.7z', browser_download_url: 'nope' },] },
    ]})

    let err = await assert.isRejected(provider.getLatestVersion())
    assert.match(err.message, /release/)
    assert.match(err.message, /found/)
  })

  // --

  
  t.test('should return correct name and link in result when git_required_prefix is specified', async function() {
    const assertName = 'Karen'
    const assertLink = 'Over The Future'
    const assertFilename = 'gittest_test-sc.7z'
    let stubber = stub()
    let provider = new GitProvider({ git_required_prefix: 'gittest' }, stubber)

    stubber.resolves({ body: [
      { name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
    ]})

    let version = await provider.getLatestVersion()
    assert.strictEqual(version.version, assertName)
    assert.strictEqual(version.link, assertLink)
    assert.strictEqual(version.filename, assertFilename)
    assert.strictEqual(version.log, '')
  })

  t.test('should skip zip files for now even when git_required_prefix is specified', async function() {
    const assertName = 'Karen'
    const assertLink = 'My Wings'
    const assertFilename = 'gittest_test-sc.zip'
    let stubber = stub()
    let provider = new GitProvider({ git_required_prefix: 'gittest' }, stubber)

    stubber.resolves({ body: [
      { name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
    ]})

    let err = await assert.isRejected(provider.getLatestVersion())
    assert.match(err.message, /release/)
    assert.match(err.message, /found/)
  })

  t.test('should skip versions with missing git_required_prefix prefix', async function() {
    const assertName = 'name1'
    const assertLink = 'somelink'
    const assertFilename = 'gittest_something-sc.7z'
    let stubber = stub()
    let provider = new GitProvider({ git_required_prefix: 'gittest' }, stubber)

    stubber.resolves({ body: [
      { name: 'test', assets: [] },
      { name: assertName, assets: [{ name: 'something-sc.7z', browser_download_url: 'nope' }] },
      { name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
    ]})

    let version = await provider.getLatestVersion()
    assert.strictEqual(version.version, assertName)
    assert.strictEqual(version.link, assertLink)
    assert.strictEqual(version.filename, assertFilename)
  })

  t.test('should skip versions with non-sc, non-git_required_prefix filename', async function() {
    const assertName = 'name2'
    const assertLink = 'somelink2'
    const assertFilename = 'gittest_something-sc.7z'
    let stubber = stub()
    let provider = new GitProvider({ git_required_prefix: 'gittest' }, stubber)

    stubber.resolves({ body: [
      { name: 'test', assets: [{ name: 'nope.7z', browser_download_url: 'nope' }] },
      { name: 'test', assets: [{ name: 'gittest_nope.7z', browser_download_url: 'nope' }] },
      { name: assertName, assets: [{ name: assertFilename, browser_download_url: assertLink }] },
    ]})

    let version = await provider.getLatestVersion()
    assert.strictEqual(version.version, assertName)
    assert.strictEqual(version.link, assertLink)
    assert.strictEqual(version.filename, assertFilename)
  })

  t.test('should skip assets with non-sc filename and non-git_required_prefix', async function() {
    const assertName = 'name3'
    const assertLink = 'somelink3'
    const assertFilename = 'gittest_something-sc.7z'
    let stubber = stub()
    let provider = new GitProvider({ git_required_prefix: 'gittest' }, stubber)

    stubber.resolves({ body: [
      { name: 'test', assets: [{ name: 'gittest_nope.7z', browser_download_url: 'nope' }] },
      { name: assertName, assets: [
        { name: 'nope.7z', browser_download_url: 'nope' },
        { name: 'gittest_nope.7z', browser_download_url: 'nope' },
        { name: 'something-sc.7z', browser_download_url: 'nope' },
        { name: assertFilename, browser_download_url: assertLink },
      ] },
    ]})

    let version = await provider.getLatestVersion()
    assert.strictEqual(version.version, assertName)
    assert.strictEqual(version.link, assertLink)
    assert.strictEqual(version.filename, assertFilename)
  })

  t.test('should otherwise reject non-git_required_prefix files', async function() {
    let stubber = stub()
    let provider = new GitProvider({ git_required_prefix: 'gittest' }, stubber)

    stubber.resolves({ body: [
      { name: 'test', assets: [{ name: 'nope.7z', browser_download_url: 'nope' }] },
      { name: 'test2', assets: [{ name: 'nope2.7z', browser_download_url: 'nope' },] },
      { name: 'test3', assets: [{ name: 'nope2.7z', browser_download_url: 'nope' },] },
      { name: 'test3', assets: [{ name: 'something-sc.7z', browser_download_url: 'nope' },] },
    ]})

    let err = await assert.isRejected(provider.getLatestVersion())
    assert.match(err.message, /release/)
    assert.match(err.message, /found/)
  })

  t.test('should otherwise reject', async function() {
    let stubber = stub()
    let provider = new GitProvider({}, stubber)

    stubber.resolves({ body: [
      { name: 'test', assets: [{ name: 'nope.7z', browser_download_url: 'nope' }] },
      { name: 'test2', assets: [{ name: 'nope2.7z', browser_download_url: 'nope' },] },
      { name: 'test3', assets: [{ name: 'nope2.7z', browser_download_url: 'nope' },] },
    ]})

    let err = await assert.isRejected(provider.getLatestVersion())
    assert.match(err.message, /release/)
    assert.match(err.message, /found/)
  })

  t.test('should reject if not valid body', async function() {
    let provider = new GitProvider({}, stub())

    provider.requester.resolves({ body: { } })

    let err = await assert.isRejected(provider.getLatestVersion())
    assert.match(err.message, /valid/)
    assert.match(err.message, /repository/)
    assert.match(err.message, /release/)
  })

  t.test('should wrap rejected errors', async function() {
    const assertError = new Error('Oosegoto')
    let provider = new GitProvider({}, stub())

    provider.requester.rejects(assertError)

    let err = await assert.isRejected(provider.getLatestVersion())
    assert.notStrictEqual(err, assertError)
    assert.match(err.message, /failed/i)
    assert.match(err.message, /release/i)
    assert.match(err.message, new RegExp(assertError.message))
  })
})

t.describe('#downloadVersion()', function() {
  t.test('should call requester with correct url and info', async function() {
    const assertError = new Error('Moeyuru Monmon Mousou Zou')
    const assertConfig = { a: 1 }
    const assertUrl = 'http://test'
    const assertTarget = '/some/path/here'

    let provider = new GitProvider({}, stub())
    provider.requestConfig = assertConfig
    provider.requester.rejects(assertError)

    let err = await assert.isRejected(provider.downloadVersion({
      link: assertUrl,
    }, assertTarget))
    assert.strictEqual(err, assertError)
    assert.strictEqual(provider.requester.firstCall[0], assertConfig)
    assert.strictEqual(provider.requester.firstCall[1], assertUrl)
    assert.strictEqual(provider.requester.firstCall[2], assertTarget)
  })
})

t.describe('#getRequestConfig()', function() {
  t.test('should default return empty object', function() {
    assert.deepStrictEqual(GitProvider.getRequestConfig({}), {})
    assert.deepStrictEqual(GitProvider.getRequestConfig({ asdf: '1234' }), {})
    assert.deepStrictEqual(GitProvider.getRequestConfig({ url: 'http://test' }), {})
    assert.deepStrictEqual(GitProvider.getRequestConfig({ a: 1 }), {})
  })

  t.test('should return token if token is defined', function() {
    const assertToken = 'asdf1234'
    assert.deepStrictEqual(GitProvider.getRequestConfig({ token: assertToken }), { token: assertToken })
    assert.deepStrictEqual(GitProvider.getRequestConfig({ token: assertToken, asdf: '1234' }), { token: assertToken })
    assert.deepStrictEqual(GitProvider.getRequestConfig({ token: assertToken, url: 'http://test' }), { token: assertToken })
    assert.deepStrictEqual(GitProvider.getRequestConfig({ token: assertToken, a: 1 }), { token: assertToken })
  })
})

t.describe('#checkConfig()', function() {
  t.test('should fail if missing url in config', async function() {
    let err = await assert.isRejected(new GitProvider({}).checkConfig())
    assert.match(err.message, /url/i)
    assert.match(err.message, /missing/i)
  })
  
  t.test('should fail if url is something else than an url', async function() {
    let tests = [[], {}, 1234, 'asdf', 'http://']

    let errors = await Promise.all(tests.map(function(check) {
      return assert.isRejected(new GitProvider({ url: check }).checkConfig(), `Promise fulfilled with url = ${JSON.stringify(check)}`)
    }))
    for (let err of errors) {
      assert.match(err.message, /url/i)
      assert.match(err.message, /valid/i)
    }
  })

  t.test('should not call requester with correct config and url', async function() {
    const assertUrl = 'http://test'
    let provider = new GitProvider({ url: assertUrl }, stub())
    let err = await provider.checkConfig()
    assert.notOk(provider.requester.called)
  })
})
