import net from 'net'

interface JsonRpcRequest {
  jsonrpc: '2.0'
  method: string
  params?: any
  id: number
}

interface JsonRpcResponse {
  jsonrpc: '2.0'
  id: number
  result?: any
  error?: { code: number; message: string; data?: any }
  context?: any
}

export class JsonRpcClient {
  private host: string
  private port: number
  private timeout: number

  constructor(host: string = '127.0.0.1', port: number = 9504, timeout = 1000) {
    this.host = host
    this.port = port
    this.timeout = timeout
  }

  async call(method: string, params?: any): Promise<any> {
    const req: JsonRpcRequest = {
      jsonrpc: '2.0',
      method,
      params,
      id: Date.now(),
    }

    const message = JSON.stringify(req) + '\n'

    return new Promise((resolve, reject) => {
      const client = new net.Socket()
      let response = ''

      client.setEncoding('utf-8')
      const timer = setTimeout(() => {
        client.destroy()
        reject(new Error('RPC call timed out'))
      }, this.timeout)

      client.connect(this.port, this.host, () => {
        client.write(message)
      })

      client.on('data', (data) => {
        response += data.toString()
        try {
          const res: JsonRpcResponse = JSON.parse(response)
          clearTimeout(timer)
          client.end()
          if (res.error) {
            reject(new Error(res.error.message))
          } else {
            resolve(res.result)
          }
        } catch (e) {
          // wait for more data
        }
      })

      client.on('error', (err) => {
        clearTimeout(timer)
        reject(err)
      })
    })
  }
}
