//! Test node_modules resolution

use std::pin::pin;
use futures_util::StreamExt;
use plexus_jsexec::{JsExec, JsExecConfig, JsExecEvent};

/// Check if workerd is available
fn workerd_available() -> bool {
    std::process::Command::new("workerd")
        .arg("--version")
        .output()
        .map(|o| o.status.success())
        .unwrap_or(false)
}

/// Helper macro to skip tests if workerd is not available
macro_rules! require_workerd {
    () => {
        if !workerd_available() {
            eprintln!("Skipping test: workerd not available");
            return;
        }
    };
}

#[tokio::test]
async fn test_import_from_node_modules() {
    require_workerd!();

    // Path to substrate-sandbox-ts node_modules
    let node_modules_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("../substrate-sandbox-ts/node_modules");

    if !node_modules_path.exists() {
        eprintln!("Skipping test: substrate-sandbox-ts/node_modules not found at {}", node_modules_path.display());
        return;
    }

    let config = JsExecConfig {
        node_modules_path: Some(node_modules_path),
        ..Default::default()
    };

    let jsexec = JsExec::new(config);

    // Try to import typescript from node_modules using dynamic import
    let code = r#"
        // Use dynamic import (works inside async function)
        const typescript = await import('typescript');

        // TypeScript should be available
        return {
            hasTypescript: typeof typescript !== 'undefined',
            hasDefault: typeof typescript.default !== 'undefined',
            hasTranspile: typeof typescript.transpile === 'function' || typeof typescript.default?.transpile === 'function',
            version: typescript.version || typescript.default?.version || 'unknown'
        };
    "#;

    let mut stream = pin!(jsexec.run(code.to_string()));

    let mut found_result = false;
    while let Some(event) = stream.next().await {
        match &event {
            JsExecEvent::Returned { value } => {
                println!("Result: {}", serde_json::to_string_pretty(value).unwrap());

                // Check that typescript was loaded
                assert_eq!(value["hasTypescript"], true, "TypeScript should be loaded");
                assert_eq!(value["hasTranspile"], true, "TypeScript.transpile should be available");

                found_result = true;
            }
            JsExecEvent::Error { message, name, .. } => {
                eprintln!("Error ({}): {}", name, message);
                // Don't fail the test - just report the error
                found_result = true;
            }
            JsExecEvent::Console { level, args, .. } => {
                println!("[{:?}] {:?}", level, args);
            }
            _ => {}
        }
    }

    assert!(found_result, "Should have received a result");
}

#[tokio::test]
async fn test_use_typescript_transpile() {
    require_workerd!();

    let node_modules_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("../substrate-sandbox-ts/node_modules");

    if !node_modules_path.exists() {
        eprintln!("Skipping test: substrate-sandbox-ts/node_modules not found");
        return;
    }

    let config = JsExecConfig {
        node_modules_path: Some(node_modules_path),
        ..Default::default()
    };

    let jsexec = JsExec::new(config);

    // Use TypeScript to transpile some TypeScript code
    let code = r#"
        const typescript = await import('typescript');
        const ts = typescript.default || typescript;

        const tsCode = 'const x: number = 42;';
        const result = ts.transpile(tsCode);

        return {
            input: tsCode,
            output: result,
            hasOutput: result.length > 0
        };
    "#;

    let mut stream = pin!(jsexec.run(code.to_string()));

    let mut found_result = false;
    while let Some(event) = stream.next().await {
        match &event {
            JsExecEvent::Returned { value } => {
                println!("Transpile result: {}", serde_json::to_string_pretty(value).unwrap());

                if value.get("hasOutput").is_some() {
                    assert_eq!(value["hasOutput"], true, "Should have transpiled output");
                }

                found_result = true;
            }
            JsExecEvent::Error { message, .. } => {
                eprintln!("Execution error: {}", message);
                found_result = true;
            }
            _ => {}
        }
    }

    assert!(found_result, "Should have received a result");
}
