import fs from 'node:fs'
import path from 'node:path'
import type { Connect } from 'vite'

/**
 * Vite middleware that serves .rfwtxt files at /mobile/workflows/...
 *
 * Two request modes for the same URL:
 *
 * 1. **Page load (iframe)** — The browser requests the URL with `Accept: text/html`.
 *    We pass through to the next handler (mobile static middleware) which serves
 *    the Flutter SPA's index.html. The Flutter app boots, reads Uri.base.path,
 *    sees `.rfwtxt`, and fetches the file content.
 *
 * 2. **XHR/fetch (Flutter app)** — The Flutter app fetches the rfwtxt content
 *    without `Accept: text/html`. We serve the raw rfwtxt file.
 */
export function createRfwtxtMiddleware(workflowsDir: string): Connect.NextHandleFunction {
	return (req, res, next) => {
		const url = req.url
		if (!url?.startsWith('/mobile/workflows/') || !url.endsWith('.rfwtxt')) {
			return next()
		}

		// If the request accepts HTML, it's a page/iframe navigation.
		// Let the Flutter SPA handle it (served by mobile static middleware).
		const accept = req.headers.accept ?? ''
		if (accept.includes('text/html')) {
			return next()
		}

		// Otherwise it's a fetch/XHR from the Flutter app — serve raw rfwtxt.
		const relativePath = url.replace('/mobile/workflows/', '')
		const filePath = path.join(workflowsDir, relativePath)

		// Prevent path traversal
		if (!filePath.startsWith(workflowsDir)) {
			res.statusCode = 403
			res.end('Forbidden')
			return
		}

		if (!fs.existsSync(filePath)) {
			res.statusCode = 404
			res.end(`Not found: ${relativePath}`)
			return
		}

		const content = fs.readFileSync(filePath, 'utf-8')
		res.setHeader('Content-Type', 'text/plain; charset=utf-8')
		res.setHeader('Access-Control-Allow-Origin', '*')
		res.setHeader('Cache-Control', 'no-cache')
		res.end(content)
	}
}

/**
 * Vite middleware that serves the rfw-preview Flutter web app static files
 * at /mobile/. Handles:
 * - Static assets (JS, CSS, fonts, images)
 * - SPA fallback: any unmatched path under /mobile/ returns index.html
 *   (needed for Flutter's path-based routing of .rfwtxt URLs)
 */
export function createMobileStaticMiddleware(staticDir: string): Connect.NextHandleFunction {
	return (req, res, next) => {
		const url = req.url
		if (!url?.startsWith('/mobile/')) return next()

		// Map /mobile/foo/bar to staticDir/foo/bar
		let relativePath = url.replace('/mobile/', '')
		if (relativePath === '' || relativePath === '/') {
			relativePath = 'index.html'
		}

		const filePath = path.join(staticDir, relativePath)

		// Prevent path traversal
		if (!filePath.startsWith(staticDir)) {
			res.statusCode = 403
			res.end('Forbidden')
			return
		}

		// Try to serve the exact file
		if (fs.existsSync(filePath) && !fs.statSync(filePath).isDirectory()) {
			const content = fs.readFileSync(filePath)
			const ext = path.extname(filePath)
			const mimeTypes: Record<string, string> = {
				'.html': 'text/html',
				'.js': 'application/javascript',
				'.css': 'text/css',
				'.json': 'application/json',
				'.png': 'image/png',
				'.svg': 'image/svg+xml',
				'.wasm': 'application/wasm',
				'.ttf': 'font/ttf',
				'.woff': 'font/woff',
				'.woff2': 'font/woff2',
			}
			res.setHeader('Content-Type', mimeTypes[ext] ?? 'application/octet-stream')
			res.end(content)
			return
		}

		// SPA fallback: serve index.html for any unmatched path
		// (Flutter path-based routing handles .rfwtxt URLs client-side)
		const indexPath = path.join(staticDir, 'index.html')
		if (fs.existsSync(indexPath)) {
			const content = fs.readFileSync(indexPath, 'utf-8')
			res.setHeader('Content-Type', 'text/html; charset=utf-8')
			res.end(content)
			return
		}

		// No Flutter build available — pass through
		return next()
	}
}
