import http from "node:http"
import fs from "node:fs"
import path from "node:path"
import util from "node:util"
import mime from "mime"

const MIMETYPE_DEFAULT = "application/octet-stream"

const args = util.parseArgs({
  options: {
    path: { type: "string", default: "public", required: true },
    port: { type: "string", default: "3000", required: true },
    host: { type: "string", default: "0.0.0.0" },
  },
})

const host = args.values.host
const basePath = args.values.path

const port = Math.trunc(Number(args.values.port))
if (Number.isNaN(port)) {
  console.error("Error: Invalid port number.")
  process.exit(1)
}

const serverURL = `http://${host}:${port}`

const server = http.createServer(async (request, response) => {
  if (request.url == null) {
    response.writeHead(400, { "Content-Type": "text/plain" })
    response.end("Bad Request")
    return
  }
  const url = new URL(request.url, serverURL)
  const urlPath = url.pathname
  const filePath = path.join(process.cwd(), basePath, urlPath)
  try {
    const stat = await fs.promises.stat(filePath)
    if (stat.isDirectory()) {
      const indexFile = path.join(filePath, "index.html")
      try {
        const fileContent = await fs.promises.readFile(indexFile)
        response.writeHead(200, { "Content-Type": "text/html" })
        response.end(fileContent)
      } catch {
        response.writeHead(403, { "Content-Type": "text/plain" })
        response.end("Error: Directory listing not allowed.")
      }
    } else {
      const mimeType = mime.getType(filePath) ?? MIMETYPE_DEFAULT
      const fileContent = await fs.promises.readFile(filePath)
      response.writeHead(200, { "Content-Type": mimeType })
      response.end(fileContent)
    }
  } catch (error) {
    if (error instanceof Error && "code" in error && error.code === "ENOENT") {
      response.writeHead(404, { "Content-Type": "text/plain" })
      response.end("Error: File not found.")
    } else {
      response.writeHead(500, { "Content-Type": "text/plain" })
      response.end("Error: Internal Server Error.")
    }
  }
})

const gracefulShutdown = (): void => {
  server.close()
  process.exit(0)
}
process.on("SIGTERM", gracefulShutdown)
process.on("SIGINT", gracefulShutdown)

server.listen(
  {
    host,
    port,
  },
  () => {
    console.log(`HTTP Server is listening at ${util.styleText("cyan", serverURL)}`)
    console.log(`Serving files from: \`${basePath}\``)
  },
)
