use std::fmt::Write as _;
use std::process::ExitCode;

/// A single DLP regex rule for network-layer MCP detection.
///
/// Derived from RFC-0132 §Shadow-MCP-Detection, Layer 3 and the Cloudflare
/// DLP pattern reference in the RFC-0132 Appendix. These patterns match
/// MCP JSON-RPC messages as they appear in HTTP request/response bodies.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DlpRule {
    /// Human-readable rule name.
    pub name: &'static str,
    /// Selector category from RFC-0132 (host / uri / body).
    pub category: &'static str,
    /// Regex pattern compatible with grep -E and most SIEM/WAF engines.
    pub regex: &'static str,
    /// Free-text description for the operator.
    pub description: &'static str,
}

/// All MCP DLP rules derived from RFC-0132 Appendix and Cloudflare reference.
pub const DLP_RULES: &[DlpRule] = &[
    DlpRule {
        name: "MCP Initialize Method",
        category: "body",
        regex: r#""method"\s{0,5}:\s{0,5}"initialize""#,
        description: "MCP init handshake — first message in every MCP session",
    },
    DlpRule {
        name: "MCP Tools Call",
        category: "body",
        regex: r#""method"\s{0,5}:\s{0,5}"tools/call""#,
        description: "Tool invocation — present in every tool execution",
    },
    DlpRule {
        name: "MCP Tools List",
        category: "body",
        regex: r#""method"\s{0,5}:\s{0,5}"tools/list""#,
        description: "Tool enumeration — emitted by clients that pre-load schema",
    },
    DlpRule {
        name: "MCP Resources Read",
        category: "body",
        regex: r#""method"\s{0,5}:\s{0,5}"resources/read""#,
        description: "MCP resource read — file/blob access",
    },
    DlpRule {
        name: "MCP Resources List",
        category: "body",
        regex: r#""method"\s{0,5}:\s{0,5}"resources/list""#,
        description: "MCP resource listing",
    },
    DlpRule {
        name: "MCP Prompts List or Get",
        category: "body",
        regex: r#""method"\s{0,5}:\s{0,5}"prompts/(list|get)""#,
        description: "MCP prompt enumeration or retrieval",
    },
    DlpRule {
        name: "MCP Sampling Create Message",
        category: "body",
        regex: r#""method"\s{0,5}:\s{0,5}"sampling/createMessage""#,
        description: "LLM sampling back-channel — high-privilege, monitor closely",
    },
    DlpRule {
        name: "MCP Protocol Version",
        category: "body",
        regex: r#""protocolVersion"\s{0,5}:\s{0,5}"202[4-9]"#,
        description: "MCP version negotiation — present in every initialize message",
    },
    DlpRule {
        name: "MCP Notifications Initialized",
        category: "body",
        regex: r#""method"\s{0,5}:\s{0,5}"notifications/initialized""#,
        description: "Session ready notification sent after successful handshake",
    },
    DlpRule {
        name: "MCP Roots List",
        category: "body",
        regex: r#""method"\s{0,5}:\s{0,5}"roots/list""#,
        description: "Client root-directory enumeration",
    },
];

pub(super) fn render_grep(rules: &[DlpRule]) -> String {
    let mut out = String::new();
    out.push_str("# MCP DLP patterns — shell grep\n");
    out.push_str("# Generated by: mcp-gateway doctor --shadow --shadow-format grep\n");
    out.push_str("# Source: RFC-0132 §Shadow-MCP-Detection Layer 3\n");
    out.push_str("#\n");
    out.push_str("# OPERATOR NOTE: Heuristic patterns only. The gateway does not\n");
    out.push_str("# intercept outbound traffic. Deploy in your network-layer tooling.\n");
    out.push_str("#\n");
    out.push_str("# Usage example (stream log file):\n");
    out.push_str("#   tail -f /var/log/proxy.log | grep -EP 'PATTERN'\n");
    out.push('\n');

    for rule in rules {
        let _ = writeln!(
            out,
            "# [{}] {} — {}",
            rule.category, rule.name, rule.description
        );
        let _ = writeln!(out, "grep -EP '{}'\n", rule.regex);
    }
    out
}

pub(super) fn render_nginx(rules: &[DlpRule]) -> String {
    let mut out = String::new();
    out.push_str("# MCP DLP patterns — nginx log_format / if-block snippets\n");
    out.push_str("# Generated by: mcp-gateway doctor --shadow --shadow-format nginx\n");
    out.push_str("# Source: RFC-0132 §Shadow-MCP-Detection Layer 3\n");
    out.push_str("#\n");
    out.push_str("# OPERATOR NOTE: Heuristic patterns only. These are NOT enforced by\n");
    out.push_str("# mcp-gateway itself. Place in your nginx server/location block.\n");
    out.push_str("#\n");
    out.push_str("# Requires: nginx built with PCRE support (standard in most packages).\n");
    out.push_str("# Add the map block in http {}, then reference $mcp_shadow in access_log.\n");
    out.push('\n');

    let combined: Vec<&str> = rules.iter().map(|r| r.regex).collect();
    let combined_regex = combined.join("|");

    out.push_str("# -- Combined map (1 = detected MCP traffic) --\n");
    out.push_str("map $request_body $mcp_shadow {\n");
    out.push_str("    default          0;\n");
    let _ = writeln!(out, "    ~*({combined_regex})  1;");
    out.push_str("}\n\n");

    out.push_str("# -- Or use individual if blocks inside location /mcp { ... } --\n");
    for rule in rules {
        let _ = writeln!(out, "# [{}] {}", rule.category, rule.name);
        let _ = writeln!(out, "# {}", rule.description);
        let _ = writeln!(out, "if ($request_body ~* '{}') {{", rule.regex);
        out.push_str("    # set $mcp_shadow 1; access_log ... mcp_shadow;\n");
        out.push_str("}\n\n");
    }
    out
}

pub(super) fn render_yaml(rules: &[DlpRule]) -> String {
    let mut out = String::new();
    out.push_str("# MCP DLP rules — YAML export for SIEM import\n");
    out.push_str("# Generated by: mcp-gateway doctor --shadow --shadow-format yaml\n");
    out.push_str("# Source: RFC-0132 §Shadow-MCP-Detection Layer 3\n");
    out.push_str("#\n");
    out.push_str("# OPERATOR NOTE: Heuristic patterns only. The gateway does not\n");
    out.push_str("# intercept outbound traffic. Deploy in your SIEM/firewall tooling.\n");
    out.push('\n');
    out.push_str("dlp_rules:\n");

    for rule in rules {
        let _ = writeln!(out, "  - name: \"{}\"", rule.name);
        let _ = writeln!(out, "    category: \"{}\"", rule.category);
        let escaped_regex = rule.regex.replace('\\', "\\\\").replace('"', "\\\"");
        let _ = writeln!(out, "    regex: \"{escaped_regex}\"");
        let _ = writeln!(out, "    description: \"{}\"", rule.description);
        out.push('\n');
    }
    out
}

/// Run `mcp-gateway doctor --shadow`.
///
/// Emits DLP/firewall regex rules for operator-side network-layer MCP
/// detection. This is rule generation only; the gateway does not intercept
/// arbitrary outbound traffic.
pub fn run_doctor_shadow_command(format: &str) -> ExitCode {
    let output = match format.to_ascii_lowercase().as_str() {
        "grep" | "" => render_grep(DLP_RULES),
        "nginx" | "haproxy" => render_nginx(DLP_RULES),
        "yaml" => render_yaml(DLP_RULES),
        other => {
            eprintln!(
                "Error: unknown --shadow-format '{other}'. \
                 Valid values: grep, nginx, yaml"
            );
            return ExitCode::FAILURE;
        }
    };
    print!("{output}");
    ExitCode::SUCCESS
}
