#!/usr/bin/env node

import assert from 'node:assert/strict'
import { spawn } from 'node:child_process'
import { once } from 'node:events'
import { createServer } from 'node:http'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { WebSocket } from 'ws'
import { createOntologyMcpServer } from '../../../ontology-core/src/mcp-server.mjs'

const INTERNAL_TOKEN = 'hub-ontology-mcp-smoke-token'
const PAIRING_CODE = 'ONTOLOGYSMOKE1'
const currentFile = fileURLToPath(import.meta.url)
const hubRoot = path.resolve(path.dirname(currentFile), '..', '..')

const ontologyPort = await availablePort()
const hubPort = await availablePort()
const ontologyServer = createOntologyMcpServer({
  internalTokens: [INTERNAL_TOKEN],
  allowedOrigins: [],
  maxBodyBytes: 1024 * 1024,
})
const hubProcess = spawn(process.execPath, ['src/server.mjs'], {
  cwd: hubRoot,
  env: {
    ...process.env,
    PORT: String(hubPort),
    AI_WORKSPACE_HUB_TOKEN: INTERNAL_TOKEN,
    NODEDC_INTERNAL_ACCESS_TOKEN: INTERNAL_TOKEN,
    NODEDC_ONTOLOGY_CORE_URL: `http://127.0.0.1:${ontologyPort}`,
    NODEDC_AI_WORKSPACE_ASSISTANT_URL: 'http://127.0.0.1:1',
  },
  stdio: ['ignore', 'pipe', 'pipe'],
})
let hubOutput = ''
hubProcess.stdout.on('data', (chunk) => { hubOutput += String(chunk) })
hubProcess.stderr.on('data', (chunk) => { hubOutput += String(chunk) })
let worker = null

try {
  await new Promise((resolve) => ontologyServer.listen(ontologyPort, '127.0.0.1', resolve))
  await waitForHub(`http://127.0.0.1:${hubPort}/healthz`, hubProcess)

  worker = new WebSocket(
    `ws://127.0.0.1:${hubPort}/api/ai-workspace/hub?pairingCode=${PAIRING_CODE}&machineName=ontology-smoke`,
  )
  await once(worker, 'open')
  worker.send(JSON.stringify({
    type: 'hello',
    agentVersion: 'smoke',
    protocolVersion: 'ai-workspace-bridge/v1',
    capabilities: ['smoke'],
  }))

  const response = await fetch(
    `http://127.0.0.1:${hubPort}/api/ai-workspace/hub/v1/ontology-mcp/${PAIRING_CODE}/mcp`,
    {
      method: 'POST',
      headers: {
        'content-type': 'application/json',
        accept: 'application/json, text/event-stream',
        'mcp-protocol-version': '2025-06-18',
      },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 1,
        method: 'tools/call',
        params: {
          name: 'ontology_get_entity',
          arguments: { term: 'helius' },
        },
      }),
    },
  )
  const payload = await response.json()
  assert.equal(response.ok, true)
  assert.equal(response.headers.get('mcp-protocol-version'), '2025-06-18')
  assert.equal(payload.result.structuredContent.entity.id, 'gelios.integration')

  worker.close()
  await once(worker, 'close')
  const offline = await fetch(
    `http://127.0.0.1:${hubPort}/api/ai-workspace/hub/v1/ontology-mcp/${PAIRING_CODE}/mcp`,
    { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' },
  )
  assert.equal(offline.status, 503)

  console.log(JSON.stringify({
    ok: true,
    checks: [
      'pairing_bound_hub_proxy',
      'internal_bearer_replaced_by_hub',
      'ontology_mcp_tool_response',
      'mcp_protocol_header_forwarded',
      'offline_pairing_is_rejected',
    ],
  }, null, 2))
} finally {
  if (worker && worker.readyState === WebSocket.OPEN) worker.close()
  if (hubProcess.exitCode === null && !hubProcess.killed) {
    hubProcess.kill('SIGTERM')
    await once(hubProcess, 'exit').catch(() => {})
  }
  await new Promise((resolve, reject) => ontologyServer.close((error) => error ? reject(error) : resolve()))
  if (hubProcess.exitCode && hubProcess.exitCode !== 0) {
    throw new Error(`hub_smoke_child_failed:${hubOutput.slice(-2000)}`)
  }
}

async function availablePort() {
  const server = createServer()
  await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve))
  const port = server.address().port
  await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()))
  return port
}

async function waitForHub(url, processRef) {
  for (let attempt = 0; attempt < 40; attempt += 1) {
    if (processRef.exitCode !== null) throw new Error('hub_smoke_child_exited_early')
    try {
      const response = await fetch(url)
      if (response.ok) return
    } catch {}
    await new Promise((resolve) => setTimeout(resolve, 100))
  }
  throw new Error('hub_smoke_health_timeout')
}
