/**
 * test/run_tests.js
 *
 * Node.js runner for testing generated code
 * Simulates ArkTS behavior in Node environment
 */

import { ComplexMessage } from '../test-output/test/messages/ComplexMessage.js'
import { PerformanceTest } from '../test-output/test/messages/PerformanceTest.js'
import { Metadata } from '../test-output/test/messages/Metadata.js'
import { NestedData } from '../test-output/test/messages/NestedData.js'
import { Status } from '../test-output/test/enums/Status.js'

console.log('=== Complex Features Test Suite ===\n')

// Test 1: Map fields
console.log('Test 1: Map Fields')
try {
  const msg1 = ComplexMessage.create({
    id: 'test-001',
    properties: new Map([
      ['env', 'production'],
      ['region', 'us-west-1'],
      ['version', '2.0.0']
    ]),
    counters: new Map([
      ['requests', 1000],
      ['errors', 5],
      ['success', 995]
    ]),
    metadataMap: new Map([
      ['meta1', Metadata.create({ key: 'k1', value: 'v1', timestamp: 1234567890n })],
      ['meta2', Metadata.create({ key: 'k2', value: 'v2', timestamp: 9876543210n })]
    ]),
    idToName: new Map([
      [1, 'Alice'],
      [2, 'Bob'],
      [3, 'Charlie']
    ])
  })

  console.log('  properties.size:', msg1.properties.size)
  console.log('  counters.size:', msg1.counters.size)
  console.log('  metadataMap.size:', msg1.metadataMap.size)
  console.log('  idToName.size:', msg1.idToName.size)

  const binary1 = msg1.toBinary()
  console.log('  Binary length:', binary1.length, 'bytes')

  const decoded1 = ComplexMessage.fromBinary(binary1)
  console.log('  Decoded properties.size:', decoded1.properties.size)
  console.log('  Decoded counters.get("requests"):', decoded1.counters.get('requests'))
  console.log('  Decoded metadataMap.get("meta1").key:', decoded1.metadataMap.get('meta1')?.key)
  console.log('  Decoded idToName.get(2):', decoded1.idToName.get(2))
  console.log('  ✅ Map fields work!\n')
} catch (e) {
  console.error('  ❌ Test failed:', e.message)
}

// Test 2: Oneof fields
console.log('Test 2: Oneof Fields')
try {
  const msg2a = ComplexMessage.create({
    id: 'test-002a',
    payload: { kind: 'textPayload', textPayload: 'Hello World' }
  })
  console.log('  Text payload:', msg2a.payload?.kind === 'textPayload' ? msg2a.payload.textPayload : 'N/A')

  const binary2a = msg2a.toBinary()
  const decoded2a = ComplexMessage.fromBinary(binary2a)
  console.log('  Decoded payload kind:', decoded2a.payload?.kind)
  console.log('  Decoded payload value:', decoded2a.payload?.kind === 'textPayload' ? decoded2a.payload.textPayload : 'N/A')

  const msg2b = ComplexMessage.create({
    id: 'test-002b',
    payload: { kind: 'numericPayload', numericPayload: 42 }
  })
  const binary2b = msg2b.toBinary()
  const decoded2b = ComplexMessage.fromBinary(binary2b)
  console.log('  Numeric payload:', decoded2b.payload?.kind === 'numericPayload' ? decoded2b.payload.numericPayload : 'N/A')

  console.log('  ✅ Oneof fields work!\n')
} catch (e) {
  console.error('  ❌ Test failed:', e.message)
}

// Test 3: Nested messages
console.log('Test 3: Nested Messages')
try {
  const msg3 = ComplexMessage.create({
    id: 'test-003',
    nested: NestedData.create({ name: 'nested-1', value: 100 }),
    metadataList: [
      Metadata.create({ key: 'k1', value: 'v1', timestamp: 111n }),
      Metadata.create({ key: 'k2', value: 'v2', timestamp: 222n }),
      Metadata.create({ key: 'k3', value: 'v3', timestamp: 333n })
    ]
  })

  console.log('  nested.name:', msg3.nested?.name)
  console.log('  metadataList.length:', msg3.metadataList.length)

  const binary3 = msg3.toBinary()
  const decoded3 = ComplexMessage.fromBinary(binary3)
  console.log('  Decoded nested.name:', decoded3.nested?.name)
  console.log('  Decoded nested.value:', decoded3.nested?.value)
  console.log('  Decoded metadataList[1].key:', decoded3.metadataList[1]?.key)
  console.log('  ✅ Nested messages work!\n')
} catch (e) {
  console.error('  ❌ Test failed:', e.message)
}

// Test 4: Recursive messages
console.log('Test 4: Recursive Messages (Deep Nesting)')
try {
  const msg4 = ComplexMessage.create({
    id: 'parent',
    count: 1,
    child: ComplexMessage.create({
      id: 'child-1',
      count: 2,
      child: ComplexMessage.create({
        id: 'child-2',
        count: 3,
        child: ComplexMessage.create({
          id: 'child-3',
          count: 4
        })
      })
    })
  })

  console.log('  Root id:', msg4.id)
  console.log('  Child id:', msg4.child?.id)
  console.log('  Grandchild id:', msg4.child?.child?.id)
  console.log('  Great-grandchild id:', msg4.child?.child?.child?.id)

  const binary4 = msg4.toBinary()
  console.log('  Binary length:', binary4.length, 'bytes')
  const decoded4 = ComplexMessage.fromBinary(binary4)
  console.log('  Decoded great-grandchild id:', decoded4.child?.child?.child?.id)
  console.log('  ✅ Recursive messages work!\n')
} catch (e) {
  console.error('  ❌ Test failed:', e.message)
}

// Test 5: All scalar types
console.log('Test 5: All Scalar Types')
try {
  const msg5 = ComplexMessage.create({
    id: 'scalar-test',
    count: 42,
    bigNumber: 9223372036854775807n,
    price: 99.99,
    active: true,
    data: new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF]),
    status: Status.ACTIVE,
    tags: ['tag1', 'tag2', 'tag3'],
    numbers: [1, 2, 3, 4, 5]
  })

  const binary5 = msg5.toBinary()
  const decoded5 = ComplexMessage.fromBinary(binary5)
  console.log('  count:', decoded5.count)
  console.log('  bigNumber:', decoded5.bigNumber)
  console.log('  price:', decoded5.price)
  console.log('  active:', decoded5.active)
  console.log('  data:', Array.from(decoded5.data).map(b => '0x' + b.toString(16).toUpperCase()).join(' '))
  console.log('  status:', Status[decoded5.status])
  console.log('  tags:', decoded5.tags)
  console.log('  ✅ All scalar types work!\n')
} catch (e) {
  console.error('  ❌ Test failed:', e.message)
}

// Performance tests
console.log('=== Performance Benchmarks ===\n')

function createTestMessage(id) {
  return PerformanceTest.create({
    id: `test-${id}`,
    value1: id * 1,
    value2: id * 2,
    value3: id * 3,
    value4: id * 4,
    score1: id * 1.5,
    score2: id * 2.5,
    score3: id * 3.5,
    flag1: id % 2 === 0,
    flag2: id % 3 === 0,
    tags: [`tag-${id}-1`, `tag-${id}-2`, `tag-${id}-3`],
    counters: new Map([
      ['count1', id * 10],
      ['count2', id * 20],
      ['count3', id * 30]
    ])
  })
}

// Benchmark: Encoding
console.log('Benchmark 1: Binary Encoding (1000 messages)')
const testMessages = []
for (let i = 0; i < 1000; i++) {
  testMessages.push(createTestMessage(i))
}

let startTime = Date.now()
const binaryResults = []
for (const msg of testMessages) {
  binaryResults.push(msg.toBinary())
}
let elapsed = Date.now() - startTime

const avgSize = binaryResults.reduce((sum, b) => sum + b.length, 0) / binaryResults.length
console.log(`  Time: ${elapsed}ms`)
console.log(`  Throughput: ${(testMessages.length * 1000 / elapsed).toFixed(0)} msg/sec`)
console.log(`  Avg binary size: ${avgSize.toFixed(1)} bytes\n`)

// Benchmark: Decoding
console.log('Benchmark 2: Binary Decoding (1000 messages)')
startTime = Date.now()
const decodedMessages = []
for (const binary of binaryResults) {
  decodedMessages.push(PerformanceTest.fromBinary(binary))
}
elapsed = Date.now() - startTime

console.log(`  Time: ${elapsed}ms`)
console.log(`  Throughput: ${(binaryResults.length * 1000 / elapsed).toFixed(0)} msg/sec\n`)

// Benchmark: Round-trip
console.log('Benchmark 3: Round-trip (Encode + Decode, 1000 messages)')
startTime = Date.now()
for (let i = 0; i < testMessages.length; i++) {
  const binary = testMessages[i].toBinary()
  const decoded = PerformanceTest.fromBinary(binary)
}
elapsed = Date.now() - startTime

console.log(`  Time: ${elapsed}ms`)
console.log(`  Throughput: ${(testMessages.length * 1000 / elapsed).toFixed(0)} round-trips/sec\n`)

console.log('=== All Tests Completed Successfully! ✅ ===')