import fs from 'node:fs'
import zlib from 'node:zlib'
import { runInlineTests, runVitest } from '#test-utils'
import { playwright } from '@vitest/browser-playwright'
import { parse } from 'flatted'
import { resolve } from 'pathe'
import { describe, expect, it } from 'vitest'

describe('html reporter', async () => {
  const vitestRoot = resolve(import.meta.dirname, '../../../..')
  const root = resolve(import.meta.dirname, '../../fixtures/reporters')

  it('resolves to "passing" status for test file "all-passing-or-skipped"', async () => {
    const basePath = 'html/all-passing-or-skipped'

    const { stderr } = await runVitest(
      {
        reporters: 'html',
        outputFile: `${basePath}/index.html`,
        root,
        env: { NO_COLOR: '1' },
      },
      ['all-passing-or-skipped'],
    )

    expect(stderr).toBe('')

    const metaJsonGzipped = fs.readFileSync(resolve(root, `${basePath}/html.meta.json.gz`))
    const metaJson = zlib.gunzipSync(metaJsonGzipped).toString('utf-8')
    const indexHtml = fs.readFileSync(resolve(root, `${basePath}/index.html`), { encoding: 'utf-8' })
    const resultJson = parse(metaJson.replace(new RegExp(vitestRoot, 'g'), '<rootDir>'))
    resultJson.config = {} // doesn't matter for a test
    const file = resultJson.files[0]
    // reset durations and id
    file.id = 0
    file.collectDuration = 0
    file.environmentLoad = 0
    file.prepareDuration = 0
    file.setupDuration = 0
    file.importDurations = {}
    file.result.duration = 0
    file.result.startTime = 0

    const task = file.tasks[0]
    task.id = 0
    task.result.duration = 0
    task.result.startTime = 0

    expect(task.result.errors).not.toBeDefined()
    expect(task.result.logs).not.toBeDefined()

    expect(resultJson).toMatchSnapshot(`tests are passing`)
    expect(indexHtml).toMatch('window.METADATA_PATH="html.meta.json.gz"')
  }, 120000)

  it('resolves to "failing" status for test file "json-fail"', async () => {
    const basePath = 'html/fail'

    const { stderr } = await runVitest(
      {
        reporters: 'html',
        outputFile: `${basePath}/index.html`,
        root,
        env: { NO_COLOR: '1' },
      },
      ['json-fail.test'],
    )

    expect(stderr).toBe('')

    const metaJsonGzipped = fs.readFileSync(resolve(root, `${basePath}/html.meta.json.gz`))
    const metaJson = zlib.gunzipSync(metaJsonGzipped).toString('utf-8')
    const indexHtml = fs.readFileSync(resolve(root, `${basePath}/index.html`), { encoding: 'utf-8' })
    const resultJson = parse(metaJson.replace(new RegExp(vitestRoot, 'g'), '<rootDir>'))
    resultJson.config = {} // doesn't matter for a test
    const file = resultJson.files[0]
    // reset durations and id
    file.id = 0
    file.collectDuration = 0
    file.environmentLoad = 0
    file.prepareDuration = 0
    file.setupDuration = 0
    file.importDurations = {}
    file.result.duration = 0
    file.result.startTime = 0

    const task = file.tasks[0]
    task.id = 0
    task.result.duration = 0
    task.result.startTime = 0

    expect(task.result.errors).toBeDefined()
    task.result.errors[0].stack = task.result.errors[0].stack.split('\n')[0]
    expect(task.logs).toBeDefined()
    expect(task.logs).toHaveLength(1)
    task.logs[0].taskId = 0
    task.logs[0].time = 0

    expect(resultJson).toMatchSnapshot(`tests are failing`)
    expect(indexHtml).toMatch('window.METADATA_PATH="html.meta.json.gz"')
  }, 120000)
})

it('browser mode headless', async () => {
  const result = await runInlineTests({
    'basic.test.ts': /* ts */`
import { test } from "vitest";
test('basic', () => {});
`,
  }, {
    reporters: ['default', 'html'],
    browser: {
      enabled: true,
      provider: playwright(),
      headless: true,
      instances: [
        { browser: 'chromium' as const },
      ],
    },
  })
  expect(result.stderr).toMatchInlineSnapshot(`""`)
  expect(result.errorTree()).toMatchInlineSnapshot(`
    {
      "basic.test.ts": {
        "basic": "passed",
      },
    }
  `)
  expect(result.fs.statFile('html/index.html').isFile()).toBe(true)
})

it('html and coverage already next each other', async () => {
  const result = await runInlineTests({
    'basic.ts': `
export const add = (a: number, b: number) => a + b;
`,
    'basic.test.ts': `
import { test, expect } from "vitest";
import { add } from "./basic";
test('add', () => {
  expect(add(1, 2)).toBe(3);
});
`,
  }, {
    reporters: [
      'default',
      ['html', { outputFile: './custom-dir/index.html' }],
    ],
    coverage: {
      enabled: true,
      reporter: ['html'],
      reportsDirectory: './custom-dir/coverage',
    },
  })
  expect(result.stderr).toMatchInlineSnapshot(`""`)
  expect(result.errorTree()).toMatchInlineSnapshot(`
    {
      "basic.test.ts": {
        "add": "passed",
      },
    }
  `)
  expect({
    html: result.fs.statFile('custom-dir/index.html').isFile(),
    coverage: result.fs.statFile('custom-dir/coverage/index.html').isFile(),
  }).toMatchInlineSnapshot(`
    {
      "coverage": true,
      "html": true,
    }
  `)
})
