const { expect } = require('chai')
const sinon = require('sinon')

// Load Database first so Auth resolves OidcAuthStrategy before the circular require completes.
require('../../../server/Database')
const OidcAuthStrategy = require('../../../server/auth/OidcAuthStrategy')
const Logger = require('../../../server/Logger')

describe('OidcAuthStrategy - isValidWebCallbackUrl', () => {
  /** @type {OidcAuthStrategy} */
  let strategy

  beforeEach(() => {
    global.RouterBasePath = ''
    strategy = new OidcAuthStrategy()
    sinon.stub(Logger, 'warn')
    sinon.stub(Logger, 'error')
  })

  afterEach(() => {
    sinon.restore()
  })

  function mockReq({ secure = false, host = 'books.example.com', xForwardedProto = null } = {}) {
    return {
      secure,
      get(header) {
        if (header === 'host') return host
        if (header === 'x-forwarded-proto') return xForwardedProto
        return null
      }
    }
  }

  it('accepts a same-origin relative path when router base path is empty', () => {
    expect(strategy.isValidWebCallbackUrl('/library', mockReq())).to.equal(true)
  })

  it('accepts a same-origin absolute https URL', () => {
    const req = mockReq({ secure: true })
    expect(strategy.isValidWebCallbackUrl('https://books.example.com/library', req)).to.equal(true)
  })

  it('rejects protocol-relative URLs', () => {
    expect(strategy.isValidWebCallbackUrl('//evil.example/capture', mockReq())).to.equal(false)
  })

  it('rejects backslash-prefixed URLs', () => {
    expect(strategy.isValidWebCallbackUrl('/\\evil.example/capture', mockReq())).to.equal(false)
  })

  it('rejects absolute external URLs', () => {
    expect(strategy.isValidWebCallbackUrl('http://evil.example/capture', mockReq())).to.equal(false)
  })

  it('rejects encoded protocol-relative path segments', () => {
    expect(strategy.isValidWebCallbackUrl('/%2F%2Fevil.example/capture', mockReq())).to.equal(false)
  })

  it('rejects same-origin URLs outside router base path', () => {
    global.RouterBasePath = '/audiobookshelf'
    expect(strategy.isValidWebCallbackUrl('/login', mockReq())).to.equal(false)
    expect(strategy.isValidWebCallbackUrl('/audiobookshelf/login', mockReq())).to.equal(true)
  })

  it('rejects empty and malformed callback URLs', () => {
    expect(strategy.isValidWebCallbackUrl('', mockReq())).to.equal(false)
    expect(strategy.isValidWebCallbackUrl(null, mockReq())).to.equal(false)
    expect(strategy.isValidWebCallbackUrl('not a url', mockReq())).to.equal(false)
  })

  it('uses x-forwarded-proto when determining same-origin https URLs', () => {
    const req = mockReq({ xForwardedProto: 'https' })
    expect(strategy.isValidWebCallbackUrl('https://books.example.com/login', req)).to.equal(true)
    expect(strategy.isValidWebCallbackUrl('http://books.example.com/login', req)).to.equal(false)
  })
})
