import { makeLocaleOptional, stripLookahead } from '../packages/runtime/src/helpers/matchers'

const makeDataPath = (path: string) => `/_next/data/build-id${path === '/' ? '/index' : path}.json`

function checkPath(path: string, regex: string) {
  const re = new RegExp(regex)
  const dataPath = makeDataPath(path)
  const testPath = re.test(path)
  const testData = re.test(dataPath)
  //   For easier debugging
  //   console.log({ path, regex, dataPath, testPath, testData })
  return testPath && testData
}

describe('the middleware path matcher', () => {
  it('makes the locale slug optional in the regex for the root', () => {
    // The regex generated by Next for the path "/" with i18n enabled
    const regex =
      '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))(|\\.json|\\/?index|\\/?index\\.json)?[\\/#\\?]?$'
    expect(checkPath('/', regex)).toBe(false)
    expect(checkPath('/', makeLocaleOptional(regex))).toBe(true)
    expect(checkPath('/en', makeLocaleOptional(regex))).toBe(true)
  })

  it('makes the locale slug optional in the regex for a subpath', () => {
    // The regex generated by Next for the path "/static" with i18n enabled
    const regex = '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))\\/static(.json)?[\\/#\\?]?$'
    expect(checkPath('/static', regex)).toBe(false)
    expect(checkPath('/static', makeLocaleOptional(regex))).toBe(true)
    expect(checkPath('/en/static', makeLocaleOptional(regex))).toBe(true)
  })

  it('does not change the regex when calling makeLocaleOptional with a regex that has no locale', () => {
    const regexes = [
      '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/(\\/?index|\\/?index\\.json))?[\\/#\\?]?$',
      '^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/api(?:\\/((?:[^\\/#\\?]+?)(?:\\/(?:[^\\/#\\?]+?))*))?(.json)?[\\/#\\?]?$',
      '^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/shows(?:\\/((?!99|88).*))(.json)?[\\/#\\?]?$',
    ]
    for (const regex of regexes) {
      expect(makeLocaleOptional(regex)).toBe(regex)
    }
  })

  it('removes lookaheads from the regex', () => {
    const regexes = [
      '^(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))\\/shows(?:\\/((?!99|88).*))(.json)?[\\/#\\?]?$',
      '^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/shows(?:\\/((?!99|88).*))(.json)?[\\/#\\?]?$',
    ]
    for (const regex of regexes) {
      const stripped = stripLookahead(regex)
      expect(regex).toMatch(/\(\?!/)
      expect(stripped).not.toMatch(/\(\?!/)
    }
  })
  it('converts regexes with lookaheads to stripped ones that still match at least the same paths', () => {
    const regex = '^(?:\\/(_next\\/data\\/[^/]{1,}))?\\/shows(?:\\/((?!99|88).*))(.json)?[\\/#\\?]?$'
    expect(checkPath('/shows', regex)).toBe(false)
    expect(checkPath('/shows/11', regex)).toBe(true)
    expect(checkPath('/shows/99', regex)).toBe(false)
    expect(checkPath('/shows/888', regex)).toBe(false)

    const stripped = stripLookahead(regex)
    expect(checkPath('/shows', stripped)).toBe(false)
    expect(checkPath('/shows/11', stripped)).toBe(true)
    // These will be true because the regex is not as strict as the original one
    // The strict test will be done in the JS entrypoint
    expect(checkPath('/shows/99', stripped)).toBe(true)
    expect(checkPath('/shows/888', stripped)).toBe(true)
  })
})
