/**
 * Simple static file serving middleware for Hono on Bun.
 *
 * In production, serves the built web app from packages/web/dist/.
 * Non-file routes fall back to index.html for SPA client-side routing.
 *
 * Supports a basePath prefix (e.g. /splint) — strips it from the URL
 * before resolving file paths, so the built assets (at dist/assets/*)
 * work regardless of the deployment subdirectory.
 */

import type { Hono } from 'hono';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { statSync } from 'fs';

// Resolve __dirname equivalent for ESM
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const STATIC_ROOT = join(__dirname, '..', '..', 'web', 'dist');

// MIME types for common web files
const MIME: Record<string, string> = {
  '.html': 'text/html; charset=utf-8',
  '.css': 'text/css; charset=utf-8',
  '.js': 'application/javascript; charset=utf-8',
  '.mjs': 'application/javascript; charset=utf-8',
  '.json': 'application/json; charset=utf-8',
  '.png': 'image/png',
  '.jpg': 'image/jpeg',
  '.jpeg': 'image/jpeg',
  '.svg': 'image/svg+xml',
  '.ico': 'image/x-icon',
  '.woff2': 'font/woff2',
  '.woff': 'font/woff',
};

function getMimeType(path: string): string {
  const ext = path.slice(path.lastIndexOf('.')).toLowerCase();
  return MIME[ext] || 'application/octet-stream';
}

function tryReadFile(filePath: string): Response | null {
  try {
    statSync(filePath); // throws if missing
    const file = Bun.file(filePath);
    return new Response(file, {
      headers: { 'Content-Type': getMimeType(filePath) },
    });
  } catch {
    return null;
  }
}

/**
 * Strip a base path prefix from a URL pathname.
 * e.g. stripBasePath('/splint/ledgers/abc', '/splint') → '/ledgers/abc'
 *      stripBasePath('/ledgers/abc', '/splint') → '/ledgers/abc'  (no match, unchanged)
 */
function stripBasePath(path: string, basePath: string): string {
  if (!basePath || basePath === '/' || basePath === '') return path;
  const bp = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;
  if (path.startsWith(bp)) {
    return path.slice(bp.length) || '/';
  }
  return path;
}

export function serveStatic(app: Hono, basePath: string = ''): void {
  // Catch-all route: serve static files, fallback to index.html for SPA routing
  app.get(`${basePath}/*`, (c) => {
    const url = new URL(c.req.url);
    let filePath = stripBasePath(url.pathname, basePath);

    // Strip leading slash
    if (filePath.startsWith('/')) {
      filePath = filePath.slice(1);
    }

    // Default to index.html for root
    if (!filePath || filePath === '') {
      filePath = 'index.html';
    }

    const fullPath = join(STATIC_ROOT, filePath);

    // Try the exact path first
    const exact = tryReadFile(fullPath);
    if (exact) return exact;

    // Try with .html extension (clean URLs)
    if (!filePath.includes('.')) {
      const htmlPath = fullPath + '.html';
      const htmlFile = tryReadFile(htmlPath);
      if (htmlFile) return htmlFile;
    }

    // SPA fallback: serve index.html for any non-matching route
    const index = tryReadFile(join(STATIC_ROOT, 'index.html'));
    if (index) return index;

    return c.text('Not Found', 404);
  });
}
