import type { Socket } from 'node:net'

export interface Socks5Target {
  host: string
  port: number
}

/** Minimal SOCKS5 handshake (no-auth only) + CONNECT request parser. Resolves once the
 * client's target address is known, after which the caller is expected to pipe the
 * raw bytes that follow straight through to an upstream connection. */
export function readSocks5Target(socket: Socket): Promise<Socks5Target> {
  return new Promise((resolve, reject) => {
    let stage: 'greeting' | 'request' = 'greeting'

    const onData = (chunk: Buffer) => {
      try {
        if (stage === 'greeting') {
          if (chunk[0] !== 0x05) throw new Error('Unsupported SOCKS version')
          socket.write(Buffer.from([0x05, 0x00]))
          stage = 'request'
          return
        }

        if (chunk[0] !== 0x05 || chunk[1] !== 0x01) {
          throw new Error('Only CONNECT is supported')
        }
        const addrType = chunk[3]
        let host: string
        let offset: number
        if (addrType === 0x01) {
          host = `${chunk[4]}.${chunk[5]}.${chunk[6]}.${chunk[7]}`
          offset = 8
        } else if (addrType === 0x03) {
          const len = chunk[4]
          host = chunk.subarray(5, 5 + len).toString('ascii')
          offset = 5 + len
        } else {
          throw new Error('Unsupported SOCKS5 address type')
        }
        const port = chunk.readUInt16BE(offset)
        socket.removeListener('data', onData)
        resolve({ host, port })
      } catch (err) {
        socket.removeListener('data', onData)
        reject(err instanceof Error ? err : new Error('SOCKS5 parse error'))
      }
    }

    socket.on('data', onData)
    socket.on('error', reject)
    socket.on('close', () => reject(new Error('Socket closed before SOCKS5 handshake completed')))
  })
}

export function sendSocks5Success(socket: Socket) {
  socket.write(Buffer.from([0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0]))
}

export function sendSocks5Failure(socket: Socket) {
  socket.write(Buffer.from([0x05, 0x01, 0x00, 0x01, 0, 0, 0, 0, 0, 0]))
}
