import { faker } from '@faker-js/faker'
import { beforeEach, describe, expect, it, TestRunner } from 'vitest'
import { config } from '~/composables/client'
import { page, render } from '~/test'
import ViewReport from './ViewReport.vue'

config.value.root = ''

const taskErrorTestId = 'task-error'
const viewReportTestId = 'view-report'
const stackRowTestId = 'stack'

function makeTextStack() {
  return {
    line: faker.number.int({ min: 0, max: 120 }),
    column: faker.number.int({ min: 0, max: 5000 }),
    // Absolute file paths
    file: faker.system.filePath(),
    method: faker.hacker.verb(),
  }
}

// 5 Stacks
const textStacks = Array.from({ length: 5 }, makeTextStack)

const diff = `
  \x1B[32m- Expected\x1B[39m
  \x1B[31m+ Received\x1B[39m

  \x1B[2m  Object {\x1B[22m
  \x1B[2m    "a": 1,\x1B[22m
  \x1B[32m-   "b": 2,\x1B[39m
  \x1B[31m+   "b": 3,\x1B[39m
  \x1B[2m  }\x1B[22m
`

const error = {
  name: 'Do some test',
  stacks: textStacks,
  message: 'Error: Transform failed with 1 error:',
  diff,
}

const fileWithTextStacks = TestRunner.createFileTask(
  'test/plain-stack-trace.ts',
  '',
  '',
)
fileWithTextStacks.mode = 'run'
fileWithTextStacks.result = {
  state: 'fail',
  errors: [error],
}

describe.todo('ViewReport', () => {
  describe('RunnerTestFile where stacks are in text', () => {
    beforeEach(async () => {
      await render(ViewReport, {
        props: {
          file: fileWithTextStacks,
        },
        attrs: {
          'data-testid': viewReportTestId,
        },
      })
    })

    it('renders all of the stacks', () => {
      const stacks = error.stacks
      const stacksElements = page.getByTestId(stackRowTestId)
      expect(stacksElements).toHaveLength(stacks.length)

      for (let idx = 0; idx < stacks.length; idx++) {
        const { column, line, file: fileName } = stacks[idx]
        expect(stacksElements.nth(idx)).toHaveTextContent(`${line}:${column}`)
        expect(stacksElements.nth(idx)).toHaveTextContent(`- ${fileName}`)
      }
    })

    it('renders the error message', () => {
      const report = page.getByTestId(viewReportTestId)
      expect(report).toHaveTextContent(error.message)
      expect(report).toHaveTextContent(error.name)
    })
  })

  it('test html stack trace without html message', async () => {
    const file = TestRunner.createFileTask('test/plain-stack-trace.ts', '', '')
    file.mode = 'run'
    file.result = {
      state: 'fail',
      errors: [
        {
          name: 'Do some test',
          stacks: [],
          stack: '\x1B[33mtest/plain-stack-trace.ts\x1B[0m',
          message: 'Error: Transform failed with 1 error:',
          diff,
        },
      ],
    }
    const container = await render(ViewReport, {
      props: { file },
    })
    const taskError = container.getByTestId(taskErrorTestId)
    const preElements = taskError.element().querySelectorAll('pre')
    expect(preElements).toHaveLength(1)

    expect(preElements[0].textContent, 'error has the correct plain text').toBe(
      'Do some test: Error: Transform failed with 1 error:test/plain-stack-trace.ts',
    )
    expect(
      preElements[0].children,
      'the pre container has the correct children',
    ).toHaveLength(2)

    const [bold, stack] = preElements[0].children
    expect(bold.tagName, 'error contains <b> element').toBe('B')
    expect(bold.textContent, 'the <b> error element is correct').toBe(
      'Do some test',
    )

    expect(
      stack.children,
      'the stack children elements is correct',
    ).toHaveLength(0)
    expect(stack.innerHTML, 'stack has the correct message').toBe(
      'test/plain-stack-trace.ts',
    )
    expect(
      stack.getAttribute('style'),
      'the stack has the correct text color',
    ).toBe('color:#A50')
  })

  it('test html stack trace and message', async () => {
    const file = TestRunner.createFileTask('test/plain-stack-trace.ts', '', '')
    file.mode = 'run'
    file.result = {
      state: 'fail',
      errors: [
        {
          name: 'Do some test',
          stack: '\x1B[33mtest/plain-stack-trace.ts\x1B[0m',
          stacks: [],
          message: '\x1B[44mError: Transform failed with 1 error:\x1B[0m',
          diff,
        },
      ],
    }
    const container = await render(ViewReport, {
      props: { file },
    })
    const taskError = container.getByTestId(taskErrorTestId)
    const preElements = taskError.element().querySelectorAll('pre')
    expect(preElements).toHaveLength(1)

    expect(preElements[0].textContent, 'error has the correct plain text').toBe(
      'Do some test: Error: Transform failed with 1 error:test/plain-stack-trace.ts',
    )
    expect(
      preElements[0].children,
      'the pre container has the correct children',
    ).toHaveLength(3)

    const [bold, error, stack] = preElements[0].children
    expect(bold.tagName, 'error contains <b> element').toBe('B')
    expect(bold.textContent, 'the <b> error element is correct').toBe(
      'Do some test',
    )

    expect(error.innerHTML, 'the error has the correct message').toBe(
      'Error: Transform failed with 1 error:',
    )
    expect(
      error.getAttribute('style'),
      'the error has the correct background color',
    ).toBe('background-color:#00A')

    expect(
      stack.children,
      'the stack children elements is correct',
    ).toHaveLength(0)
    expect(stack.innerHTML, 'stack has the correct message').toBe(
      'test/plain-stack-trace.ts',
    )
    expect(
      stack.getAttribute('style'),
      'the stack has the correct text color',
    ).toBe('color:#A50')
  })

  it('test diff display', async () => {
    const component = await render(ViewReport, {
      props: {
        file: fileWithTextStacks,
      },
    })

    const diffElement = component.getByTestId('diff')

    expect(diffElement.getByText(/Expected/)).toBeInTheDocument()
    expect(diffElement.getByText(/Received/)).toBeInTheDocument()
    expect(diffElement.getByText(/\\x1B/)).not.toBeInTheDocument()
  })
})
