//! mailer-bin: CLI and IPC binary for the @serve.zone/mailer Rust crates.
//!
//! Supports two modes:
//! 1. **CLI mode** — traditional subcommands for testing and standalone use
//! 2. **Management mode** (`--management`) — JSON-over-stdin/stdout IPC for
//!    integration with `@push.rocks/smartrust` from TypeScript

use clap::{Parser, Subcommand};
use serde::{Deserialize, Serialize};
use std::io::{self, BufRead, Write};
use std::net::IpAddr;

/// mailer-bin: Rust-powered email security tools
#[derive(Parser)]
#[command(name = "mailer-bin", version, about)]
struct Cli {
    #[command(subcommand)]
    command: Option<Commands>,

    /// Run in management/IPC mode (JSON-over-stdin/stdout for smartrust bridge)
    #[arg(long)]
    management: bool,
}

#[derive(Subcommand)]
enum Commands {
    /// Print version information
    Version,

    /// Validate an email address
    Validate {
        /// The email address to validate
        email: String,
    },

    /// Detect bounce type from an SMTP response
    Bounce {
        /// The SMTP response or diagnostic message
        message: String,
    },

    /// Check IP reputation via DNSBL
    CheckIp {
        /// The IP address to check
        ip: String,
    },

    /// Verify DKIM/SPF/DMARC for an email (reads raw message from stdin)
    VerifyEmail {
        /// Sender IP address for SPF check
        #[arg(long)]
        ip: Option<String>,

        /// HELO domain for SPF check
        #[arg(long)]
        helo: Option<String>,

        /// Receiving server hostname
        #[arg(long, default_value = "localhost")]
        hostname: String,

        /// MAIL FROM address for SPF check
        #[arg(long)]
        mail_from: Option<String>,
    },

    /// Sign an email with DKIM (reads raw message from stdin)
    DkimSign {
        /// Signing domain
        #[arg(long)]
        domain: String,

        /// DKIM selector
        #[arg(long, default_value = "mta")]
        selector: String,

        /// Path to RSA private key PEM file
        #[arg(long)]
        key: String,
    },
}

// --- IPC types for smartrust bridge ---

#[derive(Deserialize)]
struct IpcRequest {
    id: String,
    method: String,
    params: serde_json::Value,
}

#[derive(Serialize)]
struct IpcResponse {
    id: String,
    success: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    result: Option<serde_json::Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    error: Option<String>,
}

#[derive(Serialize)]
struct IpcEvent {
    event: String,
    data: serde_json::Value,
}

fn main() {
    let cli = Cli::parse();

    if cli.management {
        run_management_mode();
        return;
    }

    match cli.command {
        Some(Commands::Version) | None => {
            println!(
                "mailer-bin v{} (core: {}, smtp: {}, security: {})",
                env!("CARGO_PKG_VERSION"),
                mailer_core::version(),
                mailer_smtp::version(),
                mailer_security::version(),
            );
        }

        Some(Commands::Validate { email }) => {
            let result = mailer_core::validate_email(&email);
            println!(
                "{}",
                serde_json::to_string_pretty(&serde_json::json!({
                    "email": email,
                    "valid": result.is_valid,
                    "formatValid": result.format_valid,
                    "score": result.score,
                    "error": result.error_message,
                }))
                .unwrap()
            );
        }

        Some(Commands::Bounce { message }) => {
            let detection = mailer_core::detect_bounce_type(Some(&message), None, None);
            println!(
                "{}",
                serde_json::to_string_pretty(&detection).unwrap()
            );
        }

        Some(Commands::CheckIp { ip }) => {
            let rt = tokio::runtime::Runtime::new().unwrap();
            rt.block_on(async {
                let ip_addr: IpAddr = match ip.parse() {
                    Ok(addr) => addr,
                    Err(e) => {
                        eprintln!("Invalid IP address: {}", e);
                        std::process::exit(1);
                    }
                };

                let resolver = hickory_resolver::TokioResolver::builder_tokio().map(|b| b.build())
                    .expect("Failed to create DNS resolver");

                match mailer_security::check_reputation(
                    ip_addr,
                    mailer_security::DEFAULT_DNSBL_SERVERS,
                    &resolver,
                )
                .await
                {
                    Ok(result) => {
                        println!("{}", serde_json::to_string_pretty(&result).unwrap());
                    }
                    Err(e) => {
                        eprintln!("Error: {}", e);
                        std::process::exit(1);
                    }
                }
            });
        }

        Some(Commands::VerifyEmail {
            ip,
            helo,
            hostname,
            mail_from,
        }) => {
            let rt = tokio::runtime::Runtime::new().unwrap();
            rt.block_on(async {
                // Read raw message from stdin
                let mut raw_message = Vec::new();
                io::stdin()
                    .lock()
                    .read_to_end(&mut raw_message)
                    .expect("Failed to read from stdin");

                let authenticator = mailer_security::default_authenticator()
                    .expect("Failed to create authenticator");

                // DKIM verification
                let dkim_results = mailer_security::verify_dkim(&raw_message, &authenticator)
                    .await
                    .unwrap_or_else(|e| {
                        vec![mailer_security::DkimVerificationResult {
                            is_valid: false,
                            domain: None,
                            selector: None,
                            status: "error".to_string(),
                            details: Some(e.to_string()),
                        }]
                    });

                let mut output = serde_json::json!({
                    "dkim": dkim_results,
                });

                // SPF verification (if IP provided)
                if let (Some(ip_str), Some(helo_domain), Some(sender)) =
                    (&ip, &helo, &mail_from)
                {
                    if let Ok(ip_addr) = ip_str.parse::<IpAddr>() {
                        match mailer_security::check_spf(
                            ip_addr,
                            helo_domain,
                            &hostname,
                            sender,
                            &authenticator,
                        )
                        .await
                        {
                            Ok(spf_result) => {
                                output["spf"] = serde_json::to_value(&spf_result).unwrap();
                            }
                            Err(e) => {
                                output["spf"] =
                                    serde_json::json!({"error": e.to_string()});
                            }
                        }
                    }
                }

                println!("{}", serde_json::to_string_pretty(&output).unwrap());
            });
        }

        Some(Commands::DkimSign {
            domain,
            selector,
            key,
        }) => {
            // Read private key
            let key_pem = std::fs::read_to_string(&key).unwrap_or_else(|e| {
                eprintln!("Failed to read key file '{}': {}", key, e);
                std::process::exit(1);
            });

            // Read raw message from stdin
            let mut raw_message = Vec::new();
            io::stdin()
                .lock()
                .read_to_end(&mut raw_message)
                .expect("Failed to read from stdin");

            match mailer_security::sign_dkim(&raw_message, &domain, &selector, &key_pem) {
                Ok(header) => {
                    // Output signed message: DKIM header + original message
                    print!("{}", header);
                    io::stdout().write_all(&raw_message).unwrap();
                }
                Err(e) => {
                    eprintln!("DKIM signing failed: {}", e);
                    std::process::exit(1);
                }
            }
        }
    }
}

use std::io::Read;

/// Run in management/IPC mode for smartrust bridge.
fn run_management_mode() {
    // Signal readiness
    let ready_event = IpcEvent {
        event: "ready".to_string(),
        data: serde_json::json!({
            "version": env!("CARGO_PKG_VERSION"),
            "core_version": mailer_core::version(),
            "security_version": mailer_security::version(),
        }),
    };
    println!("{}", serde_json::to_string(&ready_event).unwrap());
    io::stdout().flush().unwrap();

    let rt = tokio::runtime::Runtime::new().unwrap();

    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        let line = match line {
            Ok(l) => l,
            Err(_) => break,
        };

        if line.trim().is_empty() {
            continue;
        }

        let req: IpcRequest = match serde_json::from_str(&line) {
            Ok(r) => r,
            Err(e) => {
                let resp = IpcResponse {
                    id: "unknown".to_string(),
                    success: false,
                    result: None,
                    error: Some(format!("Invalid request: {}", e)),
                };
                println!("{}", serde_json::to_string(&resp).unwrap());
                io::stdout().flush().unwrap();
                continue;
            }
        };

        let response = rt.block_on(handle_ipc_request(&req));
        println!("{}", serde_json::to_string(&response).unwrap());
        io::stdout().flush().unwrap();
    }
}

async fn handle_ipc_request(req: &IpcRequest) -> IpcResponse {
    match req.method.as_str() {
        "ping" => IpcResponse {
            id: req.id.clone(),
            success: true,
            result: Some(serde_json::json!({"pong": true})),
            error: None,
        },

        "version" => IpcResponse {
            id: req.id.clone(),
            success: true,
            result: Some(serde_json::json!({
                "bin": env!("CARGO_PKG_VERSION"),
                "core": mailer_core::version(),
                "security": mailer_security::version(),
                "smtp": mailer_smtp::version(),
            })),
            error: None,
        },

        "validateEmail" => {
            let email = req.params.get("email").and_then(|v| v.as_str()).unwrap_or("");
            let result = mailer_core::validate_email(email);
            IpcResponse {
                id: req.id.clone(),
                success: true,
                result: Some(serde_json::json!({
                    "valid": result.is_valid,
                    "formatValid": result.format_valid,
                    "score": result.score,
                    "error": result.error_message,
                })),
                error: None,
            }
        }

        "detectBounce" => {
            let smtp_response = req.params.get("smtpResponse").and_then(|v| v.as_str());
            let diagnostic = req.params.get("diagnosticCode").and_then(|v| v.as_str());
            let status = req.params.get("statusCode").and_then(|v| v.as_str());
            let detection = mailer_core::detect_bounce_type(smtp_response, diagnostic, status);
            IpcResponse {
                id: req.id.clone(),
                success: true,
                result: Some(serde_json::to_value(&detection).unwrap()),
                error: None,
            }
        }

        "checkIpReputation" => {
            let ip_str = req.params.get("ip").and_then(|v| v.as_str()).unwrap_or("");
            match ip_str.parse::<IpAddr>() {
                Ok(ip_addr) => {
                    let resolver = match hickory_resolver::TokioResolver::builder_tokio().map(|b| b.build()) {
                        Ok(r) => r,
                        Err(e) => {
                            return IpcResponse {
                                id: req.id.clone(),
                                success: false,
                                result: None,
                                error: Some(format!("DNS resolver error: {}", e)),
                            };
                        }
                    };
                    match mailer_security::check_reputation(
                        ip_addr,
                        mailer_security::DEFAULT_DNSBL_SERVERS,
                        &resolver,
                    )
                    .await
                    {
                        Ok(result) => IpcResponse {
                            id: req.id.clone(),
                            success: true,
                            result: Some(serde_json::to_value(&result).unwrap()),
                            error: None,
                        },
                        Err(e) => IpcResponse {
                            id: req.id.clone(),
                            success: false,
                            result: None,
                            error: Some(e.to_string()),
                        },
                    }
                }
                Err(e) => IpcResponse {
                    id: req.id.clone(),
                    success: false,
                    result: None,
                    error: Some(format!("Invalid IP address: {}", e)),
                },
            }
        }

        "verifyDkim" => {
            let raw_message = req
                .params
                .get("rawMessage")
                .and_then(|v| v.as_str())
                .unwrap_or("");
            let authenticator = match mailer_security::default_authenticator() {
                Ok(a) => a,
                Err(e) => {
                    return IpcResponse {
                        id: req.id.clone(),
                        success: false,
                        result: None,
                        error: Some(format!("Authenticator error: {}", e)),
                    };
                }
            };
            match mailer_security::verify_dkim(raw_message.as_bytes(), &authenticator).await {
                Ok(results) => IpcResponse {
                    id: req.id.clone(),
                    success: true,
                    result: Some(serde_json::to_value(&results).unwrap()),
                    error: None,
                },
                Err(e) => IpcResponse {
                    id: req.id.clone(),
                    success: false,
                    result: None,
                    error: Some(e.to_string()),
                },
            }
        }

        "signDkim" => {
            let raw_message = req
                .params
                .get("rawMessage")
                .and_then(|v| v.as_str())
                .unwrap_or("");
            let domain = req.params.get("domain").and_then(|v| v.as_str()).unwrap_or("");
            let selector = req
                .params
                .get("selector")
                .and_then(|v| v.as_str())
                .unwrap_or("mta");
            let private_key = req
                .params
                .get("privateKey")
                .and_then(|v| v.as_str())
                .unwrap_or("");

            match mailer_security::sign_dkim(raw_message.as_bytes(), domain, selector, private_key) {
                Ok(header) => IpcResponse {
                    id: req.id.clone(),
                    success: true,
                    result: Some(serde_json::json!({
                        "header": header,
                        "signedMessage": format!("{}{}", header, raw_message),
                    })),
                    error: None,
                },
                Err(e) => IpcResponse {
                    id: req.id.clone(),
                    success: false,
                    result: None,
                    error: Some(e.to_string()),
                },
            }
        }

        "verifyEmail" => {
            let raw_message = req
                .params
                .get("rawMessage")
                .and_then(|v| v.as_str())
                .unwrap_or("");
            let ip_str = req.params.get("ip").and_then(|v| v.as_str()).unwrap_or("");
            let helo = req
                .params
                .get("heloDomain")
                .and_then(|v| v.as_str())
                .unwrap_or("");
            let hostname = req
                .params
                .get("hostname")
                .and_then(|v| v.as_str())
                .unwrap_or("localhost");
            let mail_from = req
                .params
                .get("mailFrom")
                .and_then(|v| v.as_str())
                .unwrap_or("");

            match ip_str.parse::<IpAddr>() {
                Ok(ip_addr) => {
                    let authenticator = match mailer_security::default_authenticator() {
                        Ok(a) => a,
                        Err(e) => {
                            return IpcResponse {
                                id: req.id.clone(),
                                success: false,
                                result: None,
                                error: Some(format!("Authenticator error: {}", e)),
                            };
                        }
                    };
                    match mailer_security::verify_email_security(
                        raw_message.as_bytes(),
                        ip_addr,
                        helo,
                        hostname,
                        mail_from,
                        &authenticator,
                    )
                    .await
                    {
                        Ok(result) => IpcResponse {
                            id: req.id.clone(),
                            success: true,
                            result: Some(serde_json::to_value(&result).unwrap()),
                            error: None,
                        },
                        Err(e) => IpcResponse {
                            id: req.id.clone(),
                            success: false,
                            result: None,
                            error: Some(e.to_string()),
                        },
                    }
                }
                Err(e) => IpcResponse {
                    id: req.id.clone(),
                    success: false,
                    result: None,
                    error: Some(format!("Invalid IP address: {}", e)),
                },
            }
        }

        "checkSpf" => {
            let ip_str = req.params.get("ip").and_then(|v| v.as_str()).unwrap_or("");
            let helo = req
                .params
                .get("heloDomain")
                .and_then(|v| v.as_str())
                .unwrap_or("");
            let hostname = req
                .params
                .get("hostname")
                .and_then(|v| v.as_str())
                .unwrap_or("localhost");
            let mail_from = req
                .params
                .get("mailFrom")
                .and_then(|v| v.as_str())
                .unwrap_or("");

            match ip_str.parse::<IpAddr>() {
                Ok(ip_addr) => {
                    let authenticator = match mailer_security::default_authenticator() {
                        Ok(a) => a,
                        Err(e) => {
                            return IpcResponse {
                                id: req.id.clone(),
                                success: false,
                                result: None,
                                error: Some(format!("Authenticator error: {}", e)),
                            };
                        }
                    };
                    match mailer_security::check_spf(ip_addr, helo, hostname, mail_from, &authenticator)
                        .await
                    {
                        Ok(result) => IpcResponse {
                            id: req.id.clone(),
                            success: true,
                            result: Some(serde_json::to_value(&result).unwrap()),
                            error: None,
                        },
                        Err(e) => IpcResponse {
                            id: req.id.clone(),
                            success: false,
                            result: None,
                            error: Some(e.to_string()),
                        },
                    }
                }
                Err(e) => IpcResponse {
                    id: req.id.clone(),
                    success: false,
                    result: None,
                    error: Some(format!("Invalid IP address: {}", e)),
                },
            }
        }

        _ => IpcResponse {
            id: req.id.clone(),
            success: false,
            result: None,
            error: Some(format!("Unknown method: {}", req.method)),
        },
    }
}
