import * as fs from 'fs'
import * as path from 'path'

import {NetteTesterJunitParser} from '../src/parsers/tester-junit/tester-junit-parser.js'
import {ParseOptions} from '../src/test-parser.js'
import {getReport} from '../src/report/get-report.js'
import {normalizeFilePath} from '../src/utils/path-utils.js'

import {fileURLToPath} from 'url'
import {dirname} from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

describe('tester-junit tests', () => {
  it('produces empty test run result when there are no test cases', async () => {
    const fixturePath = path.join(__dirname, 'fixtures', 'empty', 'phpunit-empty.xml')
    const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
    const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})

    const opts: ParseOptions = {
      parseErrors: true,
      trackedFiles: []
    }

    const parser = new NetteTesterJunitParser(opts)
    const result = await parser.parse(filePath, fileContent)
    expect(result.tests).toBe(0)
    expect(result.result).toBe('success')
  })

  it('report from tester-v1.7-report.xml matches snapshot', async () => {
    const fixturePath = path.join(__dirname, 'fixtures', 'nette-tester', 'tester-v1.7-report.xml')
    const outputPath = path.join(__dirname, '__outputs__', 'tester-v1.7-test-results.md')
    const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
    const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})

    const opts: ParseOptions = {
      parseErrors: true,
      trackedFiles: []
    }

    const parser = new NetteTesterJunitParser(opts)
    const result = await parser.parse(filePath, fileContent)
    expect(result).toMatchSnapshot()

    const report = getReport([result])
    fs.mkdirSync(path.dirname(outputPath), {recursive: true})
    fs.writeFileSync(outputPath, report)
  })

  it('parses tester-v1.7-report.xml correctly', async () => {
    const fixturePath = path.join(__dirname, 'fixtures', 'nette-tester', 'tester-v1.7-report.xml')
    const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
    const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})

    const opts: ParseOptions = {
      parseErrors: true,
      trackedFiles: []
    }

    const parser = new NetteTesterJunitParser(opts)
    const result = await parser.parse(filePath, fileContent)

    // Verify test counts from XML: tests="65" errors="1" skipped="3"
    expect(result.tests).toBe(65)
    expect(result.failed).toBe(1)
    expect(result.skipped).toBe(3)
    expect(result.passed).toBe(61)

    // Verify suite name uses file name
    expect(result.suites.length).toBe(1)
    expect(result.suites[0].name).toBe('tester-v1.7-report.xml')
  })

  it('groups tests by directory structure', async () => {
    const fixturePath = path.join(__dirname, 'fixtures', 'nette-tester', 'tester-v1.7-report.xml')
    const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
    const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})

    const opts: ParseOptions = {
      parseErrors: true,
      trackedFiles: []
    }

    const parser = new NetteTesterJunitParser(opts)
    const result = await parser.parse(filePath, fileContent)

    // Get all group names
    const groupNames = result.suites[0].groups.map(g => g.name)

    // Verify expected directory groups exist
    expect(groupNames).toContain('tests/Framework')
    expect(groupNames).toContain('tests/CodeCoverage')
    expect(groupNames).toContain('tests/Runner')
    expect(groupNames).toContain('tests/RunnerOutput')
  })

  it('parses test names with method suffixes correctly', async () => {
    const fixturePath = path.join(__dirname, 'fixtures', 'nette-tester', 'tester-v1.7-report.xml')
    const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
    const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})

    const opts: ParseOptions = {
      parseErrors: true,
      trackedFiles: []
    }

    const parser = new NetteTesterJunitParser(opts)
    const result = await parser.parse(filePath, fileContent)

    // Find the Framework group which has tests with method suffixes
    const frameworkGroup = result.suites[0].groups.find(g => g.name === 'tests/Framework')
    expect(frameworkGroup).toBeDefined()

    // Find tests with method suffixes
    const testWithMethod = frameworkGroup!.tests.find(t => t.name.includes('::testSimple'))
    expect(testWithMethod).toBeDefined()
    expect(testWithMethod!.name).toBe('Assert.equal.recursive.phpt::testSimple')
  })

  it('parses complex test names from BootstrapFormRenderer-report.xml', async () => {
    const fixturePath = path.join(__dirname, 'fixtures', 'nette-tester', 'BootstrapFormRenderer-report.xml')
    const outputPath = path.join(__dirname, '__outputs__', 'tester-bootstrap-test-results.md')
    const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
    const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})

    const opts: ParseOptions = {
      parseErrors: true,
      trackedFiles: []
    }

    const parser = new NetteTesterJunitParser(opts)
    const result = await parser.parse(filePath, fileContent)

    // Verify test counts: 4 tests, all passed
    expect(result.tests).toBe(4)
    expect(result.passed).toBe(4)
    expect(result.failed).toBe(0)
    expect(result.skipped).toBe(0)

    // Verify suite name
    expect(result.suites[0].name).toBe('BootstrapFormRenderer-report.xml')

    // All tests should have method names
    const allTests = result.suites[0].groups.flatMap(g => g.tests)
    expect(allTests.every(t => t.name.includes('::'))).toBe(true)
    expect(allTests.some(t => t.name.includes('::testRenderingBasics'))).toBe(true)
    expect(allTests.some(t => t.name.includes('::testRenderingIndividual'))).toBe(true)

    expect(result).toMatchSnapshot()

    const report = getReport([result])
    fs.mkdirSync(path.dirname(outputPath), {recursive: true})
    fs.writeFileSync(outputPath, report)
  })

  it('extracts error details from failures', async () => {
    const fixturePath = path.join(__dirname, 'fixtures', 'nette-tester', 'tester-v1.7-report.xml')
    const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
    const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})

    const opts: ParseOptions = {
      parseErrors: true,
      trackedFiles: []
    }

    const parser = new NetteTesterJunitParser(opts)
    const result = await parser.parse(filePath, fileContent)

    // Find the failed test
    const allTests = result.suites[0].groups.flatMap(g => g.tests)
    const failedTests = allTests.filter(t => t.result === 'failed')

    expect(failedTests.length).toBe(1)

    // Verify error details are captured
    const failedTest = failedTests[0]
    expect(failedTest.error).toBeDefined()
    expect(failedTest.error!.details).toContain('Failed:')
    expect(failedTest.error!.details).toContain('multiple-fails')
  })

  it('correctly identifies skipped tests', async () => {
    const fixturePath = path.join(__dirname, 'fixtures', 'nette-tester', 'tester-v1.7-report.xml')
    const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
    const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})

    const opts: ParseOptions = {
      parseErrors: true,
      trackedFiles: []
    }

    const parser = new NetteTesterJunitParser(opts)
    const result = await parser.parse(filePath, fileContent)

    // Find skipped tests
    const allTests = result.suites[0].groups.flatMap(g => g.tests)
    const skippedTests = allTests.filter(t => t.result === 'skipped')

    expect(skippedTests.length).toBe(3)

    // Verify some known skipped tests
    expect(skippedTests.some(t => t.name.includes('Dumper.toPhp.php7.phpt'))).toBe(true)
    expect(skippedTests.some(t => t.name.includes('Collector.start.phpt'))).toBe(true)
  })

  it('parses test with description prefix correctly', async () => {
    const fixturePath = path.join(__dirname, 'fixtures', 'nette-tester', 'tester-v1.7-report.xml')
    const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
    const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})

    const opts: ParseOptions = {
      parseErrors: true,
      trackedFiles: []
    }

    const parser = new NetteTesterJunitParser(opts)
    const result = await parser.parse(filePath, fileContent)

    // Find test with description prefix
    const allTests = result.suites[0].groups.flatMap(g => g.tests)
    // The test name is generated from the basename, and the description is shown in parentheses
    const testWithDescription = allTests.find(t => t.name.includes('Prevent loop'))

    expect(testWithDescription).toBeDefined()
    expect(testWithDescription!.name).toContain('Prevent loop')
    expect(testWithDescription!.name).toContain('TestCase.ownErrorHandler.phpt')
  })
})
