import { APPS_DIR } from '$apps'
import { existsSync, readdirSync, statSync } from 'fs'
import { join } from 'path'

export async function serveStatic(appName: string, req: Request): Promise<Response> {
  const url = new URL(req.url)
  const pathname = decodeURIComponent(url.pathname)
  const pubDir = join(APPS_DIR, appName, 'pub')

  // Resolve the file path, preventing directory traversal
  const filePath = join(pubDir, pathname)
  if (!filePath.startsWith(pubDir)) {
    return new Response('Forbidden', { status: 403 })
  }

  // Directory: try index.html, then file listing
  if (existsSync(filePath) && statSync(filePath).isDirectory()) {
    const indexPath = join(filePath, 'index.html')
    if (existsSync(indexPath)) {
      return new Response(Bun.file(indexPath))
    }
    return fileListing(appName, pathname, filePath)
  }

  // Exact file match
  if (existsSync(filePath)) {
    return new Response(Bun.file(filePath))
  }

  // Clean URLs: try .html extension
  const htmlPath = filePath + '.html'
  if (existsSync(htmlPath)) {
    return new Response(Bun.file(htmlPath))
  }

  return new Response('Not Found', { status: 404 })
}

function fileListing(appName: string, pathname: string, dirPath: string): Response {
  const trail = pathname.endsWith('/') ? pathname : pathname + '/'

  const entries = readdirSync(dirPath, { withFileTypes: true })
    .filter(e => !e.name.startsWith('.'))
    .sort((a, b) => {
      if (a.isDirectory() && !b.isDirectory()) return -1
      if (!a.isDirectory() && b.isDirectory()) return 1
      return a.name.localeCompare(b.name)
    })

  const rows = entries.map(e => {
    const display = e.isDirectory() ? `${e.name}/` : e.name
    const href = `${trail}${e.name}`
    const stat = statSync(join(dirPath, e.name))
    const size = e.isDirectory() ? '—' : formatSize(stat.size)
    return `    <tr><td><a href="${href}">${display}</a></td><td>${size}</td></tr>`
  }).join('\n')

  const parent = pathname !== '/'
    ? `    <tr><td><a href="${trail.replace(/[^/]+\/$/, '')}">..</a></td><td></td></tr>\n`
    : ''

  const html = `<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>${appName} — ${pathname}</title>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 800px; margin: 2rem auto; padding: 0 1rem; }
    h1 { font-size: 1.25rem; }
    table { width: 100%; border-collapse: collapse; }
    td { padding: 0.4rem 0.8rem; border-bottom: 1px solid #eee; }
    td:last-child { text-align: right; color: #666; }
    a { color: #0366d6; text-decoration: none; }
    a:hover { text-decoration: underline; }
  </style>
</head>
<body>
  <h1>${appName}${pathname}</h1>
  <table>
${parent}${rows}
  </table>
</body>
</html>`

  return new Response(html, { headers: { 'content-type': 'text/html' } })
}

const formatSize = (bytes: number): string =>
  bytes < 1024 ? `${bytes} B`
    : bytes < 1024 * 1024 ? `${(bytes / 1024).toFixed(1)} KB`
    : `${(bytes / (1024 * 1024)).toFixed(1)} MB`
