use std::fs;
use std::path::Path;

pub fn generate(name: &str, output: &Path) -> Result<(), String> {
    let crate_name = sanitize_name(name);
    let dir = output.join(&crate_name);

    if dir.exists() {
        return Err(format!("directory already exists: {}", dir.display()));
    }

    let src_dir = dir.join("src");
    fs::create_dir_all(&src_dir).map_err(|e| format!("create dir: {e}"))?;

    // Cargo.toml
    let cargo_toml = format!(
        r#"[package]
name        = "{crate_name}"
version     = "0.1.0"
edition     = "2021"
description = "quicproquo bot: {name}"
license     = "MIT"

[dependencies]
quicproquo-bot = {{ git = "https://github.com/nickvidal/quicproquo" }}
tokio           = {{ version = "1", features = ["macros", "rt-multi-thread"] }}
anyhow          = "1"
tracing         = "0.1"
tracing-subscriber = {{ version = "0.3", features = ["env-filter"] }}
"#,
        crate_name = crate_name,
        name = name,
    );
    write_file(&dir.join("Cargo.toml"), &cargo_toml)?;

    // src/main.rs
    let main_rs = format!(
        r#"//! quicproquo bot: {name}
//!
//! A bot that connects to a quicproquo server and responds to messages.
//!
//! Usage:
//!   {crate_name} --server 127.0.0.1:7000 --username my-bot --password secret
//!
//! Environment variables (alternative to CLI args):
//!   QPQ_SERVER, QPQ_USERNAME, QPQ_PASSWORD, QPQ_CA_CERT, QPQ_STATE_PATH

use quicproquo_bot::{{Bot, BotConfig}};

#[tokio::main]
async fn main() -> anyhow::Result<()> {{
    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| "info".into()),
        )
        .init();

    // --- Configuration ---
    let server = env_or("QPQ_SERVER", "127.0.0.1:7000");
    let username = env_or("QPQ_USERNAME", "{crate_name}");
    let password = env_or("QPQ_PASSWORD", "changeme");
    let ca_cert = env_or("QPQ_CA_CERT", "server-cert.der");
    let state_path = env_or("QPQ_STATE_PATH", "{crate_name}-state.bin");

    let config = BotConfig::new(&server, &username, &password)
        .ca_cert(&ca_cert)
        .state_path(&state_path);

    // --- Connect and authenticate ---
    tracing::info!("connecting to {{server}} as {{username}}...");
    let bot = Bot::connect(config).await?;
    tracing::info!("authenticated as {{}} (key: {{}})", bot.username(), &bot.identity_key_hex()[..16]);

    // --- Main loop: poll for messages and respond ---
    tracing::info!("listening for messages (Ctrl+C to stop)...");
    loop {{
        let messages = bot.receive(5000).await?;
        for msg in messages {{
            tracing::info!("[{{}}] {{}}", msg.sender, msg.text);

            // --- Add your command handlers here ---
            if let Some(response) = handle_message(&msg.sender, &msg.text) {{
                bot.send_dm(&msg.sender, &response).await?;
            }}
        }}
    }}
}}

/// Process an incoming message and optionally return a response.
///
/// Add your bot's command logic here.
fn handle_message(sender: &str, text: &str) -> Option<String> {{
    let text = text.trim();

    // !help — list available commands
    if text == "!help" {{
        return Some(
            "Available commands:\n\
             !help  — show this message\n\
             !echo <text> — echo back the text\n\
             !whoami — show your username\n\
             !ping  — pong!"
                .to_string(),
        );
    }}

    // !echo <text> — echo back
    if let Some(rest) = text.strip_prefix("!echo ") {{
        return Some(rest.to_string());
    }}

    // !whoami — tell the sender their username
    if text == "!whoami" {{
        return Some(format!("You are {{sender}}"));
    }}

    // !ping — respond with pong
    if text == "!ping" {{
        return Some("pong!".to_string());
    }}

    // Unknown command or regular message — no response
    None
}}

fn env_or(key: &str, default: &str) -> String {{
    std::env::var(key).unwrap_or_else(|_| default.to_string())
}}
"#,
        name = name,
        crate_name = crate_name,
    );
    write_file(&src_dir.join("main.rs"), &main_rs)?;

    // README
    let readme = format!(
        r#"# {name} — quicproquo bot

## Quick start

```bash
# Build
cargo build

# Run (make sure a qpq server is running)
QPQ_SERVER=127.0.0.1:7000 \
QPQ_USERNAME={crate_name} \
QPQ_PASSWORD=changeme \
QPQ_CA_CERT=path/to/server-cert.der \
cargo run
```

## Commands

| Command | Description |
|---------|-------------|
| `!help` | Show available commands |
| `!echo <text>` | Echo back the text |
| `!whoami` | Show your username |
| `!ping` | Respond with "pong!" |

## Adding commands

Edit the `handle_message` function in `src/main.rs`:

```rust
fn handle_message(sender: &str, text: &str) -> Option<String> {{
    if text == "!mycommand" {{
        return Some("my response".to_string());
    }}
    None
}}
```

## Pipe mode

For shell integration, use the Bot SDK's JSON pipe mode:

```bash
echo '{{"action":"send","to":"alice","text":"hello"}}' | my-bot
echo '{{"action":"recv","timeout_ms":5000}}' | my-bot
```

## Documentation

- [Bot SDK docs](https://github.com/nickvidal/quicproquo/blob/main/docs/src/getting-started/bot-sdk.md)
- [Server Hooks](https://github.com/nickvidal/quicproquo/blob/main/docs/src/internals/server-hooks.md)
"#,
        name = name,
        crate_name = crate_name,
    );
    write_file(&dir.join("README.md"), &readme)?;

    println!("Created bot project: {}", dir.display());
    println!();
    println!("  cd {crate_name}");
    println!("  # Edit src/main.rs to add your commands");
    println!("  QPQ_SERVER=127.0.0.1:7000 QPQ_PASSWORD=secret cargo run");
    println!();
    println!("The bot responds to !help, !echo, !whoami, !ping out of the box.");

    Ok(())
}

fn sanitize_name(name: &str) -> String {
    name.replace(['-', ' '], "_")
}

fn write_file(path: &Path, content: &str) -> Result<(), String> {
    fs::write(path, content).map_err(|e| format!("write {}: {e}", path.display()))
}
