const requireInject = require('require-inject')
const { test } = require('tap')

let log = ''

const token = '24528a24f240'
const profile = {}
const read = {}
const legacy = requireInject('../../../lib/auth/legacy.js', {
  npmlog: {
    info: (...msgs) => {
      log += msgs.join(' ')
    },
  },
  'npm-profile': profile,
  '../../../lib/utils/open-url.js': (npm, url, msg) => {
    if (!url)
      throw Object.assign(new Error('failed open url'), { code: 'ERROR' })
  },
  '../../../lib/utils/read-user-info.js': read,
})

const npm = {
  config: {
    get: () => null,
  },
}

test('login using username/password with token result', async (t) => {
  profile.login = () => {
    return { token }
  }

  const {
    message,
    newCreds,
  } = await legacy(npm, {
    creds: {
      username: 'u',
      password: 'p',
      email: 'u@npmjs.org',
      alwaysAuth: false,
    },
    registry: 'https://registry.npmjs.org/',
    scope: '',
  })

  t.equal(
    message,
    'Logged in as u on https://registry.npmjs.org/.',
    'should have correct message result'
  )

  t.equal(
    log,
    'login Authorized user u',
    'should have correct message result'
  )

  t.deepEqual(
    newCreds,
    { token },
    'should return expected obj from profile.login'
  )

  log = ''
  delete profile.login
})

test('login using username/password with user info result', async (t) => {
  profile.login = () => {
    return null
  }

  const {
    message,
    newCreds,
  } = await legacy(npm, {
    creds: {
      username: 'u',
      password: 'p',
      email: 'u@npmjs.org',
      alwaysAuth: false,
    },
    registry: 'https://registry.npmjs.org/',
    scope: '',
  })

  t.equal(
    message,
    'Logged in as u on https://registry.npmjs.org/.',
    'should have correct message result'
  )

  t.deepEqual(
    newCreds,
    {
      username: 'u',
      password: 'p',
      email: 'u@npmjs.org',
      alwaysAuth: false,
    },
    'should return used credentials'
  )

  log = ''
  delete profile.login
})

test('login otp requested', async (t) => {
  t.plan(5)

  profile.login = () => Promise.reject(Object.assign(
    new Error('needs otp'),
    { code: 'EOTP' }
  ))
  profile.loginCouch = (username, password, { otp }) => {
    t.equal(username, 'u', 'should use provided username to loginCouch')
    t.equal(password, 'p', 'should use provided password to loginCouch')
    t.equal(otp, '1234', 'should use provided otp code to loginCouch')

    return { token }
  }
  read.otp = () => Promise.resolve('1234')

  const {
    message,
    newCreds,
  } = await legacy(npm, {
    creds: {
      username: 'u',
      password: 'p',
      email: 'u@npmjs.org',
      alwaysAuth: false,
    },
    registry: 'https://registry.npmjs.org/',
    scope: '',
  })

  t.equal(
    message,
    'Logged in as u on https://registry.npmjs.org/.',
    'should have correct message result'
  )

  t.deepEqual(
    newCreds,
    { token },
    'should return token from loginCouch result'
  )

  log = ''
  delete profile.login
  delete profile.loginCouch
  delete read.otp
})

test('login missing basic credential info', async (t) => {
  profile.login = () => Promise.reject(Object.assign(
    new Error('missing info'),
    { code: 'ERROR' }
  ))

  await t.rejects(
    legacy(npm, {
      creds: {
        username: 'u',
        password: 'p',
      },
      registry: 'https://registry.npmjs.org/',
      scope: '',
    }),
    { code: 'ERROR' },
    'should throw server response error'
  )

  log = ''
  delete profile.login
})

test('create new user when user not found', async (t) => {
  t.plan(6)

  profile.login = () => Promise.reject(Object.assign(
    new Error('User does not exist'),
    { code: 'ERROR' }
  ))
  profile.adduserCouch = (username, email, password) => {
    t.equal(username, 'u', 'should use provided username to adduserCouch')
    t.equal(email, 'u@npmjs.org', 'should use provided email to adduserCouch')
    t.equal(password, 'p', 'should use provided password to adduserCouch')

    return { token }
  }

  const {
    message,
    newCreds,
  } = await legacy(npm, {
    creds: {
      username: 'u',
      password: 'p',
      email: 'u@npmjs.org',
      alwaysAuth: false,
    },
    registry: 'https://registry.npmjs.org/',
    scope: '',
  })

  t.equal(
    message,
    'Logged in as u on https://registry.npmjs.org/.',
    'should have correct message result'
  )

  t.equal(
    log,
    'login Authorized user u',
    'should have correct message result'
  )

  t.deepEqual(
    newCreds,
    { token },
    'should return expected obj from profile.login'
  )

  log = ''
  delete profile.adduserCouch
  delete profile.login
})

test('prompts for user info if required', async (t) => {
  t.plan(4)

  profile.login = async (opener, prompt, opts) => {
    t.equal(opts.creds.alwaysAuth, true, 'should have refs to creds if any')
    await opener('https://registry.npmjs.org/-/v1/login')
    const creds = await prompt(opts.creds)
    return creds
  }
  read.username = () => Promise.resolve('foo')
  read.password = () => Promise.resolve('pass')
  read.email = () => Promise.resolve('foo@npmjs.org')

  const {
    message,
    newCreds,
  } = await legacy(npm, {
    creds: {
      alwaysAuth: true,
    },
    registry: 'https://registry.npmjs.org/',
    scope: '',
  })

  t.equal(
    message,
    'Logged in as foo on https://registry.npmjs.org/.',
    'should have correct message result'
  )

  t.equal(
    log,
    'login Authorized user foo',
    'should have correct message result'
  )

  t.deepEqual(
    newCreds,
    {
      username: 'foo',
      password: 'pass',
      email: 'foo@npmjs.org',
      alwaysAuth: true,
    },
    'should return result from profile.login containing prompt info'
  )

  log = ''
  delete profile.login
  delete read.username
  delete read.password
  delete read.email
})

test('request otp when creating new user', async (t) => {
  t.plan(3)

  profile.login = () => Promise.reject(Object.assign(
    new Error('User does not exist'),
    { code: 'ERROR' }
  ))
  profile.adduserCouch = () => Promise.reject(Object.assign(
    new Error('needs otp'),
    { code: 'EOTP' }
  ))
  profile.loginCouch = (username, password, { otp }) => {
    t.equal(username, 'u', 'should use provided username to loginCouch')
    t.equal(password, 'p', 'should use provided password to loginCouch')
    t.equal(otp, '1234', 'should now use provided otp code to loginCouch')

    return { token }
  }
  read.otp = () => Promise.resolve('1234')

  await legacy(npm, {
    creds: {
      username: 'u',
      password: 'p',
      email: 'u@npmjs.org',
      alwaysAuth: false,
    },
    registry: 'https://registry.npmjs.org/',
    scope: '',
  })

  log = ''
  delete profile.adduserCouch
  delete profile.login
  delete profile.loginCouch
  delete read.otp
})

test('unknown error during user creation', async (t) => {
  profile.login = () => Promise.reject(Object.assign(
    new Error('missing info'),
    { code: 'ERROR' }
  ))
  profile.adduserCouch = () => Promise.reject(Object.assign(
    new Error('unkown error'),
    { code: 'ERROR' }
  ))

  await t.rejects(
    legacy(npm, {
      creds: {
        username: 'u',
        password: 'p',
        email: 'u@npmjs.org',
        alwaysAuth: false,
      },
      registry: 'https://registry.npmjs.org/',
      scope: '',
    }),
    { code: 'ERROR' },
    'should throw unknown error'
  )

  log = ''
  delete profile.adduserCouch
  delete profile.login
})

test('open url error', async (t) => {
  profile.login = async (opener, prompt, opts) => {
    await opener()
  }

  await t.rejects(
    legacy(npm, {
      creds: {
        username: 'u',
        password: 'p',
      },
      registry: 'https://registry.npmjs.org/',
      scope: '',
    }),
    { message: 'failed open url', code: 'ERROR' },
    'should throw unknown error'
  )

  log = ''
  delete profile.login
})

test('login no credentials provided', async (t) => {
  profile.login = () => ({ token })

  await legacy(npm, {
    creds: {
      username: undefined,
      password: undefined,
      email: undefined,
      alwaysAuth: undefined,
    },
    registry: 'https://registry.npmjs.org/',
    scope: '',
  })

  t.equal(
    log,
    'login Authorized',
    'should have correct message result'
  )

  log = ''
  delete profile.login
})

test('scoped login', async (t) => {
  profile.login = () => ({ token })

  const { message } = await legacy(npm, {
    creds: {
      username: 'u',
      password: 'p',
      email: 'u@npmjs.org',
      alwaysAuth: false,
    },
    registry: 'https://diff-registry.npmjs.org/',
    scope: 'myscope',
  })

  t.equal(
    message,
    'Logged in as u to scope myscope on https://diff-registry.npmjs.org/.',
    'should have correct message result'
  )

  t.equal(
    log,
    'login Authorized user u',
    'should have correct message result'
  )

  log = ''
  delete profile.login
})
