mod common;

use anyhow::Context;
use kodegen_mcp_schema::filesystem::*;
use serde_json::json;
use tracing::info;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Initialize logging
    tracing_subscriber::fmt().with_env_filter("info").init();

    info!("Starting filesystem tools example");

    // Connect to kodegen server with filesystem category
    let (conn, mut server) =
        common::connect_to_local_http_server().await?;

    // Wrap client with logging
    let workspace_root = common::find_workspace_root()
        .context("Failed to find workspace root")?;
    let log_path = workspace_root.join("tmp/mcp-client/filesystem.log");
    let client = common::LoggingClient::new(conn.client(), log_path)
        .await
        .context("Failed to create logging client")?;

    info!("Connected to server: {:?}", client.server_info());

    // Run example with cleanup
    let result = run_filesystem_example(&client).await;

    // Always close connection, regardless of example result
    conn.close().await?;
    server.shutdown().await?;

    // Propagate any error from the example
    result
}

async fn run_filesystem_example(client: &common::LoggingClient) -> anyhow::Result<()> {
    // Create a temporary test directory
    let test_dir = std::env::temp_dir().join("kodegen_test");
    let test_file = test_dir.join("test.txt");

    info!("Using test directory: {}", test_dir.display());

    // Run tests
    let test_result = async {
        // 1. CREATE_DIRECTORY - Create test directory
        info!("1. Testing create_directory");
        client
            .call_tool(
                FS_CREATE_DIRECTORY,
                json!({ "path": test_dir.to_string_lossy() }),
            )
            .await
            .context("Failed to create test directory")?;
        info!("✅ Created test directory");

        // 2. WRITE_FILE - Write initial content
        info!("2. Testing write_file");
        client
            .call_tool(
                FS_WRITE_FILE,
                json!({
                    "path": test_file.to_string_lossy(),
                    "content": "Hello, kodegen!\nThis is a test file.\nLine 3",
                    "mode": "rewrite"
                }),
            )
            .await
            .context("Failed to write initial file content")?;
        info!("✅ Wrote initial file content");

        // 3. READ_FILE - Read the file back
        info!("3. Testing read_file");
        client
            .call_tool(
                FS_READ_FILE,
                json!({ "path": test_file.to_string_lossy() }),
            )
            .await
            .context("Failed to read file")?;
        info!("✅ Read file successfully");

        // 4. GET_FILE_INFO - Get metadata
        info!("4. Testing get_file_info");
        client
            .call_tool(
                FS_GET_FILE_INFO,
                json!({ "path": test_file.to_string_lossy() }),
            )
            .await
            .context("Failed to get file info")?;
        info!("✅ Got file info successfully");

        // 5. EDIT_BLOCK - Edit the file
        info!("5. Testing edit_block");
        client
            .call_tool(
                FS_EDIT_BLOCK,
                json!({
                    "path": test_file.to_string_lossy(),
                    "old_string": "test file",
                    "new_string": "modified file"
                }),
            )
            .await
            .context("Failed to edit file")?;
        info!("✅ Edited file successfully");

        // 6. LIST_DIRECTORY - List directory contents
        info!("6. Testing list_directory");
        client
            .call_tool(
                FS_LIST_DIRECTORY,
                json!({ "path": test_dir.to_string_lossy() }),
            )
            .await
            .context("Failed to list directory")?;
        info!("✅ Listed directory successfully");

        // 7. MOVE_FILE - Rename the file
        let test_file_renamed = test_dir.join("test_renamed.txt");
        info!("7. Testing move_file");
        client
            .call_tool(
                FS_MOVE_FILE,
                json!({
                    "source": test_file.to_string_lossy(),
                    "destination": test_file_renamed.to_string_lossy()
                }),
            )
            .await
            .context("Failed to move file")?;
        info!("✅ Moved file successfully");

        // 8. READ_MULTIPLE_FILES - Read multiple files
        let test_file2 = test_dir.join("test2.txt");
        client
            .call_tool(
                FS_WRITE_FILE,
                json!({
                    "path": test_file2.to_string_lossy(),
                    "content": "Second test file",
                    "mode": "rewrite"
                }),
            )
            .await
            .context("Failed to create test file 2")?;
        info!("✅ Created test file 2");

        info!("8. Testing read_multiple_files");
        client
            .call_tool(
                FS_READ_MULTIPLE_FILES,
                json!({
                    "paths": [
                        test_file_renamed.to_string_lossy(),
                        test_file2.to_string_lossy()
                    ]
                }),
            )
            .await
            .context("Failed to read multiple files")?;
        info!("✅ Read multiple files successfully");

        // 9. DELETE_FILE - Delete test files
        info!("9. Testing delete_file");
        for file in [&test_file_renamed, &test_file2] {
            client
                .call_tool(
                    FS_DELETE_FILE,
                    json!({ "path": file.to_string_lossy() }),
                )
                .await
                .context("Failed to delete file")?;
            info!("✅ Deleted file: {}", file.display());
        }

        // 10. DELETE_DIRECTORY - Clean up test directory (as part of test)
        info!("10. Testing delete_directory");
        client
            .call_tool(
                FS_DELETE_DIRECTORY,
                json!({
                    "path": test_dir.to_string_lossy(),
                    "recursive": true
                }),
            )
            .await
            .context("Failed to delete directory")?;
        info!("✅ Deleted directory successfully");

        info!("Filesystem tools example tests completed");
        Ok::<(), anyhow::Error>(())
    }
    .await;

    // Always cleanup test directory, regardless of test result
    cleanup_filesystem_resources(client, &test_dir).await;

    // Propagate test result
    test_result
}

async fn cleanup_filesystem_resources(client: &common::LoggingClient, test_dir: &std::path::Path) {
    use tracing::error;

    info!("\nCleaning up test directory...");

    // Check if directory exists before attempting cleanup
    if !test_dir.exists() {
        info!(
            "✅ Test directory already cleaned up: {}",
            test_dir.display()
        );
        return;
    }

    // Try to delete using the filesystem tool first
    if let Err(e) = client
        .call_tool(
            FS_DELETE_DIRECTORY,
            json!({
                "path": test_dir.to_string_lossy(),
                "recursive": true
            }),
        )
        .await
    {
        error!(
            "⚠️  Failed to delete directory via tool {}: {}",
            test_dir.display(),
            e
        );

        // Fall back to direct filesystem removal
        if let Err(e) = std::fs::remove_dir_all(test_dir) {
            error!(
                "⚠️  Failed to remove directory {}: {}",
                test_dir.display(),
                e
            );
            error!("   Manual cleanup required: rm -rf {}", test_dir.display());
        } else {
            info!(
                "✅ Cleaned up test directory via fallback: {}",
                test_dir.display()
            );
        }
    } else {
        info!("✅ Cleaned up test directory: {}", test_dir.display());
    }
}
