pub fn generate(name: &str) -> Result<(), String> {
    let snake = to_snake_case(name);
    let camel = name.to_string();
    println!("=== Adding RPC method: {camel} ===");
    println!();
    println!("Follow these steps to add a new `{camel}` RPC method.");
    println!("Each step shows the file and the code to add.");
    println!();

    // Step 1: Schema
    println!("--- Step 1: Cap'n Proto schema ---");
    println!("File: schemas/node.capnp");
    println!();
    println!("Add to the `interface NodeService` block:");
    println!();
    println!(
        r#"  {camel} @N (auth :AuthContext, <your params here>) -> (<your results here>);
"#,
    );
    println!("  (Replace @N with the next ordinal number in the interface.)");
    println!();
    println!("Then rebuild the proto crate:");
    println!("  cargo build -p quicproquo-proto");
    println!();

    // Step 2: Handler module
    println!("--- Step 2: Handler module ---");
    println!("File: crates/quicproquo-server/src/node_service/{snake}.rs");
    println!();
    println!(
        r#"use capnp::capability::Promise;
use quicproquo_proto::node_capnp::node_service;

use crate::auth::{{coded_error, validate_auth_context}};
use crate::error_codes::*;

use super::NodeServiceImpl;

impl NodeServiceImpl {{
    pub fn handle_{snake}(
        &mut self,
        params: node_service::{camel}Params,
        mut results: node_service::{camel}Results,
    ) -> Promise<(), capnp::Error> {{
        let p = match params.get() {{
            Ok(p) => p,
            Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
        }};
        let auth_ctx = match validate_auth_context(&self.auth_cfg, &self.sessions, p.get_auth()) {{
            Ok(ctx) => ctx,
            Err(e) => return Promise::err(e),
        }};

        // TODO: implement your logic here

        Promise::ok(())
    }}
}}
"#,
    );

    // Step 3: Wire into mod.rs
    println!("--- Step 3: Register in mod.rs ---");
    println!("File: crates/quicproquo-server/src/node_service/mod.rs");
    println!();
    println!("Add to the module declarations at the top:");
    println!("  mod {snake};");
    println!();
    println!("Add to the `impl node_service::Server for NodeServiceImpl` block:");
    println!();
    println!(
        r#"    fn {snake}(
        &mut self,
        params: node_service::{camel}Params,
        results: node_service::{camel}Results,
    ) -> capnp::capability::Promise<(), capnp::Error> {{
        self.handle_{snake}(params, results)
    }}
"#,
    );

    // Step 4: Storage (if needed)
    println!("--- Step 4: Storage trait (if needed) ---");
    println!("File: crates/quicproquo-server/src/storage.rs");
    println!();
    println!("If your RPC method needs persistent storage, add a method to the Store trait:");
    println!();
    println!(
        r#"    fn {snake}(&self, /* params */) -> Result</* return */, StorageError>;
"#,
    );
    println!("Then implement it in:");
    println!("  - crates/quicproquo-server/src/sql_store.rs (SQLite backend)");
    println!("  - crates/quicproquo-server/src/storage.rs   (FileBackedStore)");
    println!();

    // Step 5: Hook (if needed)
    println!("--- Step 5: Hook event (optional) ---");
    println!("If you want plugins to observe this RPC, run:");
    println!("  qpq-gen hook {snake}");
    println!();

    // Step 6: Verify
    println!("--- Step 6: Verify ---");
    println!("  cargo build -p quicproquo-server");
    println!("  cargo test -p quicproquo-server");
    println!();

    // Summary
    println!("=== Files to create/modify ===");
    println!("  [modify] schemas/node.capnp");
    println!("  [create] crates/quicproquo-server/src/node_service/{snake}.rs");
    println!("  [modify] crates/quicproquo-server/src/node_service/mod.rs");
    println!("  [modify] crates/quicproquo-server/src/storage.rs (if needed)");
    println!("  [modify] crates/quicproquo-server/src/sql_store.rs (if needed)");

    Ok(())
}

fn to_snake_case(s: &str) -> String {
    let mut result = String::with_capacity(s.len() + 4);
    for (i, ch) in s.chars().enumerate() {
        if ch.is_uppercase() && i > 0 {
            result.push('_');
        }
        result.push(ch.to_ascii_lowercase());
    }
    result
}
