import { beforeAll, describe, expect, test } from 'vitest'
import {
  browserLogs,
  findAssetFile,
  getBg,
  getColor,
  isBuild,
  page,
} from '~utils'

const absoluteAssetMatch = isBuild
  ? /http.*\/other-assets\/asset-[-\w]{8}\.png/
  : '/nested/asset.png'

// Asset URLs in CSS are relative to the same dir, the computed
// style returns the absolute URL in the test
const cssBgAssetMatch = absoluteAssetMatch

const iconMatch = `/icon.png`

const absoluteIconMatch = isBuild
  ? /http.*\/icon-[-\w]{8}\.png/
  : '/nested/icon.png'

const absolutePublicIconMatch = isBuild ? /http.*\/icon\.png/ : '/icon.png'

test('should have no 404s', () => {
  browserLogs.forEach((msg) => {
    expect(msg).not.toMatch('404')
  })
})

describe('raw references from /public', () => {
  test('load raw js from /public', async () => {
    expect(await page.textContent('.raw-js')).toMatch('[success]')
  })

  test('load raw css from /public', async () => {
    expect(await getColor('.raw-css')).toBe('red')
  })
})

test('import-expression from simple script', async () => {
  expect(await page.textContent('.import-expression')).toMatch(
    '[success][success]',
  )
})

describe('asset imports from js', () => {
  test('relative', async () => {
    expect(await page.textContent('.asset-import-relative')).toMatch(
      cssBgAssetMatch,
    )
  })

  test('absolute', async () => {
    expect(await page.textContent('.asset-import-absolute')).toMatch(
      cssBgAssetMatch,
    )
  })

  test('from /public', async () => {
    expect(await page.textContent('.public-import')).toMatch(
      absolutePublicIconMatch,
    )
  })
})

describe('css url() references', () => {
  test('fonts', async () => {
    expect(
      await page.evaluate(() => {
        return (document as any).fonts.check('700 32px Inter')
      }),
    ).toBe(true)
  })

  test('relative', async () => {
    const bg = await getBg('.css-url-relative')
    expect(bg).toMatch(cssBgAssetMatch)
  })

  test('image-set relative', async () => {
    const imageSet = await getBg('.css-image-set-relative')
    imageSet.split(', ').forEach((s) => {
      expect(s).toMatch(cssBgAssetMatch)
    })
  })

  test('image-set without the url() call', async () => {
    const imageSet = await getBg('.css-image-set-without-url-call')
    imageSet.split(', ').forEach((s) => {
      expect(s).toMatch(cssBgAssetMatch)
    })
  })

  test('image-set with var', async () => {
    const imageSet = await getBg('.css-image-set-with-var')
    imageSet.split(', ').forEach((s) => {
      expect(s).toMatch(cssBgAssetMatch)
    })
  })

  test('image-set with mix', async () => {
    const imageSet = await getBg('.css-image-set-mix-url-var')
    imageSet.split(', ').forEach((s) => {
      expect(s).toMatch(cssBgAssetMatch)
    })
  })

  test('relative in @import', async () => {
    expect(await getBg('.css-url-relative-at-imported')).toMatch(
      cssBgAssetMatch,
    )
  })

  test('absolute', async () => {
    expect(await getBg('.css-url-absolute')).toMatch(cssBgAssetMatch)
  })

  test('from /public', async () => {
    expect(await getBg('.css-url-public')).toMatch(iconMatch)
  })

  test('multiple urls on the same line', async () => {
    const bg = await getBg('.css-url-same-line')
    expect(bg).toMatch(cssBgAssetMatch)
    expect(bg).toMatch(iconMatch)
  })

  test('aliased', async () => {
    const bg = await getBg('.css-url-aliased')
    expect(bg).toMatch(cssBgAssetMatch)
  })

  test('nested manual chunks', async () => {
    const bg = await getBg('.css-manual-chunks-relative')
    expect(bg).toMatch(cssBgAssetMatch)
  })
})

describe.runIf(isBuild)('index.css URLs', () => {
  let css: string
  beforeAll(() => {
    css = findAssetFile(/index.*\.css$/, 'relative-base', 'other-assets')
  })

  test('relative asset URL', () => {
    expect(css).toMatch(`./asset-`)
  })

  test('preserve postfix query/hash', () => {
    expect(css).toMatch(`woff2?#iefix`)
  })
})

describe('image', () => {
  test('srcset', async () => {
    const img = await page.$('.img-src-set')
    const srcset = await img.getAttribute('srcset')
    srcset.split(', ').forEach((s) => {
      expect(s).toMatch(
        isBuild
          ? /other-assets\/asset-[-\w]{8}\.png \dx/
          : /\.\/nested\/asset\.png \dx/,
      )
    })
  })
})

describe('svg fragments', () => {
  // 404 is checked already, so here we just ensure the urls end with #fragment
  test('img url', async () => {
    const img = await page.$('.svg-frag-img')
    expect(await img.getAttribute('src')).toMatch(/svg#icon-clock-view$/)
  })

  test('via css url()', async () => {
    expect(await getBg('.icon')).toMatch(/svg#icon-clock-view"\)$/)
  })

  test('from js import', async () => {
    const img = await page.$('.svg-frag-import')
    expect(await img.getAttribute('src')).toMatch(/svg#icon-heart-view$/)
  })
})

test('?raw import', async () => {
  expect(await page.textContent('.raw')).toMatch('SVG')
})

test('?url import', async () => {
  expect(await page.textContent('.url')).toMatch(
    isBuild ? /http.*\/other-assets\/foo-[-\w]{8}\.js/ : `/foo.js`,
  )
})

test('?url import on css', async () => {
  const txt = await page.textContent('.url-css')
  expect(txt).toMatch(
    isBuild ? /http.*\/other-assets\/icons-[-\w]{8}\.css/ : '/css/icons.css',
  )
  isBuild &&
    expect(findAssetFile(/index.*\.js$/, 'relative-base', 'entries')).toMatch(
      /icons-.+\.css(?!\?used)/,
    )
})

test('new URL(..., import.meta.url)', async () => {
  const absoluteImgMatch = isBuild
    ? /http.*\/other-assets\/img-[-\w]{8}\.png/
    : '/import-meta-url/img.png'
  expect(await page.textContent('.import-meta-url')).toMatch(absoluteImgMatch)
})

test('new URL(`${dynamic}`, import.meta.url)', async () => {
  const dynamic1 = await page.textContent('.dynamic-import-meta-url-1')
  expect(dynamic1).toMatch(absoluteIconMatch)
  const dynamic2 = await page.textContent('.dynamic-import-meta-url-2')
  expect(dynamic2).toMatch(absoluteAssetMatch)
})

test('new URL(`non-existent`, import.meta.url)', async () => {
  expect(await page.textContent('.non-existent-import-meta-url')).toMatch(
    '/non-existent',
  )
})

test('inline style test', async () => {
  expect(await getBg('.inline-style')).toMatch(cssBgAssetMatch)
  expect(await getBg('.style-url-assets')).toMatch(cssBgAssetMatch)
})

test('html import word boundary', async () => {
  expect(await page.textContent('.obj-import-express')).toMatch(
    'ignore object import prop',
  )
  expect(await page.textContent('.string-import-express')).toMatch('no load')
})

test('relative path in html asset', async () => {
  expect(await page.textContent('.relative-js')).toMatch('hello')
  expect(await getColor('.relative-css')).toMatch('red')
})
