import { createServer } from 'node:http'
import { createReadStream, statSync } from 'node:fs'
import { resolve, extname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { Readable } from 'node:stream'

const __dirname = fileURLToPath(new URL('.', import.meta.url))
const { default: server } = await import('./.output/server/server.js')

const PORT = process.env.PORT || 3000
const STATIC_DIR = resolve(__dirname, '.output/client')

const MIME_TYPES = {
  '.js':   'application/javascript',
  '.mjs':  'application/javascript',
  '.css':  'text/css',
  '.html': 'text/html',
  '.json': 'application/json',
  '.png':  'image/png',
  '.jpg':  'image/jpeg',
  '.jpeg': 'image/jpeg',
  '.svg':  'image/svg+xml',
  '.ico':  'image/x-icon',
  '.woff': 'font/woff',
  '.woff2':'font/woff2',
  '.ttf':  'font/ttf',
  '.mp4':  'video/mp4',
  '.webp': 'image/webp',
}

function serveStatic(req, res) {
  try {
    const urlPath = new URL(req.url, 'http://localhost').pathname
    const filePath = join(STATIC_DIR, urlPath)
    const stat = statSync(filePath)
    if (!stat.isFile()) return false

    const ext = extname(filePath)
    res.setHeader('Content-Type', MIME_TYPES[ext] || 'application/octet-stream')
    res.setHeader('Content-Length', stat.size)
    // Cache hashed assets forever, others for 1 hour
    if (/\.[a-f0-9]{8,}\.\w+$/.test(filePath)) {
      res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
    } else {
      res.setHeader('Cache-Control', 'public, max-age=3600')
    }
    createReadStream(filePath).pipe(res)
    return true
  } catch {
    return false
  }
}

function nodeToWebRequest(req) {
  const protocol = req.socket?.encrypted ? 'https' : 'http'
  const url = `${protocol}://${req.headers.host}${req.url}`

  const headers = new Headers()
  for (const [key, value] of Object.entries(req.headers)) {
    if (value) headers.set(key, Array.isArray(value) ? value.join(', ') : value)
  }

  const init = { method: req.method, headers }

  if (!['GET', 'HEAD'].includes(req.method)) {
    init.body = Readable.toWeb(req)
    init.duplex = 'half'
  }

  return new Request(url, init)
}

async function sendWebResponse(webResponse, res) {
  res.statusCode = webResponse.status
  res.statusMessage = webResponse.statusText

  webResponse.headers.forEach((value, key) => {
    res.setHeader(key, value)
  })

  if (webResponse.body) {
    Readable.fromWeb(webResponse.body).pipe(res)
  } else {
    res.end()
  }
}

const httpServer = createServer(async (req, res) => {
  // Serve static files first
  if (serveStatic(req, res)) return

  // Fall through to SSR handler
  try {
    const webRequest = nodeToWebRequest(req)
    const webResponse = await server.fetch(webRequest)
    await sendWebResponse(webResponse, res)
  } catch (err) {
    console.error('Request error:', err)
    res.statusCode = 500
    res.end('Internal Server Error')
  }
})

httpServer.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}`)
})
