import fs from 'fs'
import https from 'https'
import path from 'path'
import net from 'net'
import http from 'http'
import tls from 'tls'
import { CertAuthServerBase } from './cert-helper.js'
const options = {
  key: fs.readFileSync(path.join(...CertAuthServerBase, 'server_key.pem')),
  cert: fs.readFileSync(path.join(...CertAuthServerBase, 'server_cert.pem')),
  requestCert: true,
  rejectUnauthorized: false,
  ca: [fs.readFileSync(path.join(...CertAuthServerBase, 'server_cert.pem'))],
}

export default class {
  #lastSocketKey = 0
  #socketMap: Record<string, net.Socket> = {}
  #runningServer?: https.Server

  async start(port: number): Promise<void> {
    return new Promise((resolve) => {
      this.#runningServer = https.createServer(options, this.#connectedCallback.bind(this))
      this.#runningServer.listen(port, () => {
        // console.log(`Server is ready on port ${httpPort}`);
        resolve()
      })
      this.#runningServer.on('connection', this.#handleConnection.bind(this))
    })
  }

  async stop(): Promise<void> {
    const keys = Object.keys(this.#socketMap) as string[]
    keys.forEach((socketKey) => {
      if (this.#socketMap[socketKey].destroyed) {
        return
      }
      this.#socketMap[socketKey].destroy()
    })
    return new Promise((resolve) => {
      if (!this.#runningServer) {
        return
      }
      this.#runningServer.close(() => resolve())
    })
  }

  #handleConnection(socket: net.Socket): void {
    const socketKey = String(++this.#lastSocketKey)
    this.#socketMap[socketKey] = socket
    socket.once('close', () => {
      // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
      delete this.#socketMap[socketKey]
    })
  }

  /**
   * Callback for the client connection.
   *
   * @param req Node's request object
   * @param res Node's response object
   */
  #connectedCallback(req: http.IncomingMessage, res: http.ServerResponse): void {
    const socket: tls.TLSSocket = req.socket as tls.TLSSocket
    const cert = socket.getPeerCertificate()
    let status: number
    let message: unknown
    if (socket.authorized) {
      status = 200
      message = {
        authenticated: true,
        name: cert.subject.CN,
        issuer: cert.issuer.CN,
      }
    } else if (cert.subject) {
      status = 403
      message = {
        authenticated: false,
        name: cert.subject.CN,
        issuer: cert.issuer.CN,
      }
    } else {
      status = 401
      message = {
        authenticated: false,
      }
    }
    res.writeHead(status, {
      'Content-Type': 'application/json; charset=UTF-8',
    })
    res.end(JSON.stringify(message))
  }
}
