---
/**
 * Sandbox Plugin Test Page
 *
 * Tests the full sandbox architecture:
 * 1. PluginBridge WorkerEntrypoint provides controlled DB access
 * 2. Sandbox gets a SERVICE BINDING to the bridge (not direct DB access)
 * 3. Bridge validates capabilities and scopes operations
 */

interface TestResult {
	step: string;
	success: boolean;
	data?: unknown;
	error?: string;
}

const results: TestResult[] = [];

// Get Cloudflare context
const cfContext = (Astro.locals as unknown as Record<string, unknown>).cfContext;
// @ts-ignore - env typing
const env = (await import("cloudflare:workers")).env;

// @ts-ignore
const loader = env.LOADER;

if (!loader) {
	results.push({ step: "Check LOADER binding", success: false, error: "LOADER not available" });
} else {
	results.push({ step: "Check LOADER binding", success: true });
}

if (!cfContext) {
	results.push({ step: "Check cfContext", success: false, error: "cfContext not available" });
} else {
	results.push({ step: "Check cfContext", success: true });
}

// Check for ctx.exports (requires enable_ctx_exports compatibility flag)
// @ts-ignore
const exports = cfContext?.exports;
if (!exports) {
	results.push({ step: "Check ctx.exports", success: false, error: "ctx.exports not available - need enable_ctx_exports flag" });
} else {
	results.push({ step: "Check ctx.exports", success: true });
}

// Check for PluginBridge export
// @ts-ignore
const PluginBridge = exports?.PluginBridge;
if (!PluginBridge) {
	results.push({ step: "Check PluginBridge export", success: false, error: "PluginBridge not in ctx.exports" });
} else {
	results.push({ step: "Check PluginBridge export", success: true });
}

// Test the bridge directly (without sandbox first)
if (PluginBridge) {
	try {
		// Create a bridge instance with props
		const bridge = PluginBridge({
			props: {
				pluginId: "test-plugin",
				pluginVersion: "1.0.0",
				capabilities: ["read:content"],
				allowedHosts: [],
				storageCollections: ["logs"],
			}
		});

		results.push({ step: "Create bridge instance", success: true });

		// Test KV operations
		try {
			await bridge.kvSet("test-key", { hello: "world" });
			const value = await bridge.kvGet("test-key");
			await bridge.kvDelete("test-key");
			results.push({ 
				step: "Bridge KV operations", 
				success: value?.hello === "world",
				data: { stored: { hello: "world" }, retrieved: value }
			});
		} catch (e) {
			results.push({ step: "Bridge KV operations", success: false, error: e instanceof Error ? e.message : String(e) });
		}

		// Test storage operations
		try {
			await bridge.storagePut("logs", "test-id", { message: "test log" });
			const value = await bridge.storageGet("logs", "test-id");
			await bridge.storageDelete("logs", "test-id");
			results.push({ 
				step: "Bridge storage operations", 
				success: value?.message === "test log",
				data: value
			});
		} catch (e) {
			results.push({ step: "Bridge storage operations", success: false, error: e instanceof Error ? e.message : String(e) });
		}

		// Test undeclared storage (should fail)
		try {
			await bridge.storageGet("undeclared", "test");
			results.push({ step: "Block undeclared storage", success: false, error: "Should have thrown" });
		} catch (e) {
			results.push({ 
				step: "Block undeclared storage", 
				success: true,
				data: { blocked: true, error: e instanceof Error ? e.message : String(e) }
			});
		}

		// Test network without capability (should fail)
		try {
			await bridge.httpFetch("https://example.com");
			results.push({ step: "Block network without capability", success: false, error: "Should have thrown" });
		} catch (e) {
			results.push({ 
				step: "Block network without capability", 
				success: true,
				data: { blocked: true, error: e instanceof Error ? e.message : String(e) }
			});
		}

	} catch (e) {
		results.push({ step: "Create bridge instance", success: false, error: e instanceof Error ? e.message : String(e) });
	}
}

// Now test the full sandbox with Worker Loader
if (loader && PluginBridge) {
	try {
		// Create a bridge binding for this specific plugin
		const bridgeBinding = PluginBridge({
			props: {
				pluginId: "sandbox-test",
				pluginVersion: "1.0.0",
				capabilities: ["read:content"],
				allowedHosts: [],
				storageCollections: ["logs"],
			}
		});

		// Sandbox code that uses the bridge
		const sandboxCode = `
			import { WorkerEntrypoint } from "cloudflare:workers";
			
			export default class PluginEntrypoint extends WorkerEntrypoint {
				async test() {
					return {
						success: true,
						message: "Hello from sandbox!",
						pluginId: this.env.PLUGIN_ID,
					};
				}
				
				async testKv() {
					const bridge = this.env.BRIDGE;
					await bridge.kvSet("sandbox-test", { from: "sandbox" });
					const value = await bridge.kvGet("sandbox-test");
					await bridge.kvDelete("sandbox-test");
					return { success: true, value };
				}
				
				async testStorage() {
					const bridge = this.env.BRIDGE;
					await bridge.storagePut("logs", "sandbox-log", { ts: Date.now() });
					const value = await bridge.storageGet("logs", "sandbox-log");
					await bridge.storageDelete("logs", "sandbox-log");
					return { success: true, value };
				}
				
				async testBlockedStorage() {
					const bridge = this.env.BRIDGE;
					try {
						await bridge.storageGet("undeclared", "test");
						return { success: false, error: "Should have been blocked" };
					} catch (e) {
						return { success: true, blocked: true, error: e.message };
					}
				}
				
				async testBlockedNetwork() {
					const bridge = this.env.BRIDGE;
					try {
						await bridge.httpFetch("https://example.com");
						return { success: false, error: "Should have been blocked" };
					} catch (e) {
						return { success: true, blocked: true, error: e.message };
					}
				}
				
				// ISOLATION TESTS - verify sandbox can't bypass bridge
				
				async testDirectFetchBlocked() {
					// Sandbox has globalOutbound: null, so fetch should fail
					try {
						const resp = await fetch("https://example.com");
						return { success: false, error: "Direct fetch should be blocked but got: " + resp.status };
					} catch (e) {
						return { success: true, blocked: true, error: e.message };
					}
				}
				
				async testNoDbBinding() {
					// Sandbox should NOT have DB binding - only BRIDGE
					const hasDb = !!this.env.DB;
					const hasMedia = !!this.env.MEDIA;
					const bindings = Object.keys(this.env);
					return { 
						success: !hasDb && !hasMedia,
						hasDb,
						hasMedia,
						bindings,
						error: hasDb || hasMedia ? "Sandbox should not have direct DB/MEDIA bindings" : null
					};
				}
				
				async testNoGlobals() {
					// Check that dangerous globals are not available
					const checks = {
						hasGlobalFetch: typeof globalThis.fetch === "function",
						// After globalOutbound: null, fetch exists but should fail
					};
					return { success: true, checks };
				}
			}
		`;

		// Spawn the sandbox with bridge binding
		const worker = loader.get("sandbox-full-test-" + Date.now(), () => ({
			compatibilityDate: "2025-01-01",
			mainModule: "plugin.js",
			modules: {
				"plugin.js": { js: sandboxCode },
			},
			globalOutbound: null, // Block direct network
			env: {
				PLUGIN_ID: "sandbox-test",
				BRIDGE: bridgeBinding, // Pass bridge as service binding
			},
		}));

		results.push({ step: "Spawn sandbox with bridge", success: true });

		// Worker Loader RPC methods are dynamically defined in sandbox code.
		// Cast entrypoint to allow calling them without TS errors.
		type SandboxRpc = Record<string, (...args: unknown[]) => Promise<Record<string, unknown>>>;
		const getEp = () => worker.getEntrypoint("default") as unknown as SandboxRpc;

		// Test basic RPC
		try {
			const ep = getEp();
			const testResult = await ep.test();
			results.push({ 
				step: "Sandbox basic RPC", 
				success: testResult?.success === true,
				data: testResult
			});
		} catch (e) {
			results.push({ step: "Sandbox basic RPC", success: false, error: e instanceof Error ? e.message : String(e) });
		}

		// Test KV via bridge
		try {
			const ep = getEp();
			const kvResult = await ep.testKv();
			results.push({ 
				step: "Sandbox KV via bridge", 
				success: kvResult?.success === true,
				data: kvResult
			});
		} catch (e) {
			results.push({ step: "Sandbox KV via bridge", success: false, error: e instanceof Error ? e.message : String(e) });
		}

		// Test storage via bridge
		try {
			const ep = getEp();
			const storageResult = await ep.testStorage();
			results.push({ 
				step: "Sandbox storage via bridge", 
				success: storageResult?.success === true,
				data: storageResult
			});
		} catch (e) {
			results.push({ step: "Sandbox storage via bridge", success: false, error: e instanceof Error ? e.message : String(e) });
		}

		// Test blocked storage
		try {
			const ep = getEp();
			const blockedResult = await ep.testBlockedStorage();
			results.push({ 
				step: "Sandbox blocked storage", 
				success: blockedResult?.blocked === true,
				data: blockedResult
			});
		} catch (e) {
			results.push({ step: "Sandbox blocked storage", success: false, error: e instanceof Error ? e.message : String(e) });
		}

		// Test blocked network
		try {
			const ep = getEp();
			const networkResult = await ep.testBlockedNetwork();
			results.push({ 
				step: "Sandbox blocked network", 
				success: networkResult?.blocked === true,
				data: networkResult
			});
		} catch (e) {
			results.push({ step: "Sandbox blocked network", success: false, error: e instanceof Error ? e.message : String(e) });
		}

		// ISOLATION TESTS - verify sandbox can't bypass bridge
		
		// Test direct fetch is blocked (globalOutbound: null)
		try {
			const ep = getEp();
			const fetchResult = await ep.testDirectFetchBlocked();
			results.push({ 
				step: "Sandbox direct fetch blocked", 
				success: fetchResult?.blocked === true,
				data: fetchResult
			});
		} catch (e) {
			results.push({ step: "Sandbox direct fetch blocked", success: false, error: e instanceof Error ? e.message : String(e) });
		}

		// Test sandbox has no DB/MEDIA bindings
		try {
			const ep = getEp();
			const bindingsResult = await ep.testNoDbBinding();
			results.push({ 
				step: "Sandbox no direct DB access", 
				success: bindingsResult?.success === true && !bindingsResult?.hasDb,
				data: bindingsResult
			});
		} catch (e) {
			results.push({ step: "Sandbox no direct DB access", success: false, error: e instanceof Error ? e.message : String(e) });
		}

	} catch (e) {
		results.push({ step: "Spawn sandbox with bridge", success: false, error: e instanceof Error ? e.message : String(e) });
	}
}

const allPassed = results.every(r => r.success);
const passCount = results.filter(r => r.success).length;
const failCount = results.filter(r => !r.success).length;
---

<html lang="en">
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<title>Sandbox Plugin Test</title>
		<style>
			body {
				font-family: system-ui, sans-serif;
				max-width: 900px;
				margin: 2rem auto;
				padding: 1rem;
				background: #1a1a1a;
				color: #e0e0e0;
			}
			h1 { color: #fff; }
			h2 { color: #ccc; margin-top: 2rem; }
			.result {
				padding: 1rem;
				border-radius: 0.5rem;
				margin: 0.75rem 0;
			}
			.success { background: #1a3d1a; border: 1px solid #2d5a2d; }
			.error { background: #3d1a1a; border: 1px solid #5a2d2d; }
			.step-name { font-weight: bold; margin-bottom: 0.5rem; }
			pre {
				background: #2a2a2a;
				padding: 1rem;
				overflow: auto;
				border-radius: 0.25rem;
				font-size: 0.85rem;
				margin: 0.5rem 0 0 0;
			}
			.summary {
				padding: 1.5rem;
				border-radius: 0.5rem;
				margin: 1rem 0;
				font-size: 1.2rem;
				text-align: center;
			}
			.summary.pass { background: #1a3d1a; border: 2px solid #4a8a4a; }
			.summary.fail { background: #3d1a1a; border: 2px solid #8a4a4a; }
			.stats { font-size: 0.9rem; margin-top: 0.5rem; color: #aaa; }
		</style>
	</head>
	<body>
		<h1>Sandbox Plugin Test</h1>

		<div class={`summary ${allPassed ? 'pass' : 'fail'}`}>
			{allPassed ? 'All Tests Passed!' : 'Some Tests Failed'}
			<div class="stats">{passCount} passed, {failCount} failed</div>
		</div>

		<h2>Infrastructure</h2>
		{results.filter(r => r.step.startsWith("Check")).map(r => (
			<div class={`result ${r.success ? 'success' : 'error'}`}>
				<div class="step-name">{r.success ? '✓' : '✗'} {r.step}</div>
				{r.error && <pre>Error: {r.error}</pre>}
				{r.data && <pre>{JSON.stringify(r.data, null, 2)}</pre>}
			</div>
		))}

		<h2>Bridge Direct Tests</h2>
		{results.filter(r => r.step.startsWith("Bridge") || r.step.startsWith("Block") || r.step === "Create bridge instance").map(r => (
			<div class={`result ${r.success ? 'success' : 'error'}`}>
				<div class="step-name">{r.success ? '✓' : '✗'} {r.step}</div>
				{r.error && <pre>Error: {r.error}</pre>}
				{r.data && <pre>{JSON.stringify(r.data, null, 2)}</pre>}
			</div>
		))}

		<h2>Sandbox Tests (via Worker Loader)</h2>
		{results.filter(r => (r.step.startsWith("Sandbox") || r.step.startsWith("Spawn")) && !r.step.includes("direct") && !r.step.includes("no direct")).map(r => (
			<div class={`result ${r.success ? 'success' : 'error'}`}>
				<div class="step-name">{r.success ? '✓' : '✗'} {r.step}</div>
				{r.error && <pre>Error: {r.error}</pre>}
				{r.data && <pre>{JSON.stringify(r.data, null, 2)}</pre>}
			</div>
		))}

		<h2>Isolation Tests (sandbox can't bypass bridge)</h2>
		{results.filter(r => r.step.includes("direct") || r.step.includes("no direct")).map(r => (
			<div class={`result ${r.success ? 'success' : 'error'}`}>
				<div class="step-name">{r.success ? '✓' : '✗'} {r.step}</div>
				{r.error && <pre>Error: {r.error}</pre>}
				{r.data && <pre>{JSON.stringify(r.data, null, 2)}</pre>}
			</div>
		))}

		<h2>Architecture</h2>
		<pre>{`
┌─────────────────────────────────────────────────────────────┐
│                    HOST WORKER (Astro)                       │
│                                                              │
│  ┌──────────────────┐     ┌─────────────────────────────┐   │
│  │  PluginBridge    │     │       EmDash CMS          │   │
│  │  (Entrypoint)    │     │                             │   │
│  │                  │     │  - Routes/Pages             │   │
│  │  - kvGet/Set     │◄────│  - Middleware               │   │
│  │  - storageQuery  │     │  - API handlers             │   │
│  │  - contentList   │     │                             │   │
│  │  - httpFetch     │     └─────────────────────────────┘   │
│  │                  │                                        │
│  │  (has DB access) │     ┌─────────────────────────────┐   │
│  └────────▲─────────┘     │     Worker Loader           │   │
│           │               │                             │   │
│           │ RPC           │  Spawns sandboxed isolates  │   │
│           │               └──────────────┬──────────────┘   │
└───────────┼──────────────────────────────┼──────────────────┘
            │                              │
            │                              ▼
┌───────────┴──────────────────────────────────────────────────┐
│                    SANDBOX ISOLATE                           │
│                                                              │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  Plugin Code                                            │ │
│  │                                                         │ │
│  │  - NO direct DB access                                  │ │
│  │  - NO direct network (globalOutbound: null)             │ │
│  │  - Only has BRIDGE service binding                      │ │
│  │                                                         │ │
│  │  ctx.kv.get() ──► env.BRIDGE.kvGet() ──► Host DB        │ │
│  │  ctx.http.fetch() ──► env.BRIDGE.httpFetch() ──► Host   │ │
│  └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
		`}</pre>
	</body>
</html>
