import { createServer } from 'node:http'
import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { createReadStream } from 'node:fs'
import { existsSync } from 'node:fs'
import { resolve, extname, join, normalize } from 'node:path'
import { spawn } from 'node:child_process'
import { createChromeProfile, removeChromeProfile } from './chrome-profile.mjs'

const root = resolve(new URL('..', import.meta.url).pathname)
const reportDir = resolve(root, '.cache/freecad/browser-occt')
await mkdir(reportDir, { recursive: true })
const contentTypes = { '.html': 'text/html', '.js': 'text/javascript', '.wasm': 'application/wasm', '.json': 'application/json' }
const reportPromises = new Map()
const reportResolvers = new Map()
const waitForReport = (browserId) => {
  if (reportPromises.has(browserId)) return reportPromises.get(browserId)
  const promise = new Promise((resolveReport, reject) => { reportResolvers.set(browserId, { resolve: resolveReport, reject }) })
  reportPromises.set(browserId, promise)
  return promise
}
const server = createServer(async (request, response) => {
  response.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
  response.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
  response.setHeader('Cross-Origin-Resource-Policy', 'same-origin')
  if (request.method === 'POST' && request.url?.startsWith('/__occt-report')) {
    let body = ''
    request.setEncoding('utf8')
    request.on('data', (chunk) => { body += chunk })
    request.on('end', async () => {
      try {
        const report = JSON.parse(body)
        const browserId = report.browserId || new URL(request.url, 'http://127.0.0.1').searchParams.get('browser') || 'unknown'
        await writeFile(resolve(reportDir, `${browserId}.json`), `${JSON.stringify(report, null, 2)}\n`)
        reportResolvers.get(browserId)?.resolve(report)
        response.writeHead(204)
        response.end()
      } catch (error) {
        response.writeHead(400)
        response.end(String(error))
      }
    })
    return
  }
  const pathname = decodeURIComponent((request.url || '/').split('?')[0])
  const relative = pathname === '/occt-browser-harness.html' ? 'public/occt-browser-harness.html' : pathname.startsWith('/native/') ? `public${pathname}` : null
  if (!relative) { response.writeHead(404); response.end('not found'); return }
  const file = normalize(join(root, relative))
  if (!file.startsWith(resolve(root, 'public')) || !existsSync(file)) { response.writeHead(404); response.end('not found'); return }
  response.setHeader('content-type', contentTypes[extname(file)] || 'application/octet-stream')
  createReadStream(file).pipe(response)
})
await new Promise((resolveServer) => server.listen(0, '127.0.0.1', resolveServer))
const port = server.address().port

const runBrowser = (browserId, executable, args) => new Promise((resolveRun) => {
  if (!executable || !existsSync(executable)) { resolveRun({ browserId, status: 'unavailable', executable }); return }
  const reportPromise = waitForReport(browserId)
  const child = spawn(executable, [...args, `http://127.0.0.1:${port}/occt-browser-harness.html?browser=${browserId}`], { cwd: root, stdio: ['ignore', 'pipe', 'pipe'] })
  let stdout = ''
  child.stdout.on('data', (chunk) => { stdout += String(chunk) })
  let stderr = ''
  child.stderr.on('data', (chunk) => { stderr += String(chunk) })
  let settled = false
  const finish = (result) => {
    if (settled) return
    settled = true
    clearTimeout(timer)
    resolveRun({ ...result, stdout: stdout.slice(-4000), stderr: stderr.slice(-2000) })
  }
  const timer = setTimeout(() => { child.kill('SIGTERM'); finish({ browserId, status: 'timeout' }) }, 240000)
  reportPromise.then((report) => {
    if (settled) return
    child.kill('SIGTERM')
    finish({ browserId, status: report.status || 'pass', report, exitCode: child.exitCode, signal: null })
  }).catch(() => {})
  child.on('close', async (code, signal) => {
    if (!settled) {
      try {
        const report = await Promise.race([reportPromise, new Promise((_, reject) => setTimeout(() => reject(new Error('report timeout')), 5000))])
        finish({ browserId, status: report.status || 'pass', report, exitCode: code, signal })
      } catch {
        finish({ browserId, status: 'failed', exitCode: code, signal })
      }
    }
  })
})

const results = []
const chromeProfile = await createChromeProfile('occt')
try {
  results.push(await runBrowser('chrome', process.env.CHROME_BIN || '/home/mes123456/.local/bin/google-chrome', ['--headless=new', '--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage', '--enable-logging=stderr', '--v=1', `--user-data-dir=${chromeProfile}`]))
} finally {
  await removeChromeProfile(chromeProfile)
}
await new Promise((resolveServer) => server.close(resolveServer))
// Keep committed evidence stable: Chrome's temporary profile paths, timestamps
// and verbose stderr are diagnostics, not compatibility claims.
const verification = {
  schemaVersion: 1,
  headers: { coop: 'same-origin', coep: 'require-corp' },
  browsers: results.map(({ stdout, stderr, ...result }) => result),
  status: results.every((result) => result.status === 'pass') ? 'pass' : 'partial',
}
await writeFile(resolve(root, 'config/occt-browser-verification.json'), `${JSON.stringify(verification, null, 2)}\n`)
console.log(JSON.stringify(verification, null, 2))
if (results.some((result) => result.status !== 'pass')) process.exit(1)
