use crate::args::Cli;
use crate::error::{RosWireError, RosWireResult};
use serde::Serialize;

pub mod cache;
pub mod discovery;
pub mod doctor;

#[derive(Debug, Clone, Serialize)]
pub struct ArgumentSpec {
    pub name: String,
    pub style: String,
    pub required: bool,
    #[serde(rename = "type")]
    pub arg_type: String,
    pub description: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub example: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
pub struct CommandDefinition {
    pub name: String,
    pub summary: String,
    pub kind: String,
    pub syntax: String,
    pub arguments: Vec<ArgumentSpec>,
    pub examples: Vec<String>,
    pub errors: Vec<String>,
}

#[derive(Debug, Serialize)]
struct CommandsPayload {
    schema_version: &'static str,
    commands: Vec<CommandSummary>,
}

#[derive(Debug, Serialize)]
struct CommandSummary {
    name: String,
    summary: String,
    kind: String,
}

#[derive(Debug, Serialize)]
struct HelpPayload {
    schema_version: &'static str,
    command: CommandDefinition,
}

#[derive(Debug, Serialize)]
struct HelpIndexPayload {
    schema_version: &'static str,
    global_options: Vec<String>,
    commands: Vec<CommandSummary>,
}

#[derive(Debug, Serialize)]
struct SchemaPayload {
    schema_version: &'static str,
    command: String,
    arguments: Vec<ArgumentSpec>,
}

#[derive(Debug, Serialize)]
struct ExplainErrorPayload {
    schema_version: &'static str,
    error_code: String,
    summary: String,
    common_causes: Vec<String>,
    suggested_next_steps: Vec<String>,
}

pub fn handle(tokens: &[String], cli: &Cli) -> Option<RosWireResult<String>> {
    if tokens.is_empty() {
        return None;
    }

    let command = tokens[0].as_str();

    if cli.remote && command == "commands" {
        return Some(Err(Box::new(RosWireError::remote_schema_unavailable())));
    }

    match command {
        "commands" => Some(render_json(&commands_payload())),
        "doctor" => Some(doctor::doctor_payload(cli)),
        "help" => Some(help_payload(tokens)),
        "schema" if cli.remote => Some(remote_schema_payload(tokens, cli)),
        "schema" => Some(schema_payload(tokens)),
        "explain-error" => Some(explain_error_payload(tokens)),
        _ => None,
    }
}

fn commands_payload() -> CommandsPayload {
    let commands = catalog()
        .into_iter()
        .map(|entry| CommandSummary {
            name: entry.name,
            summary: entry.summary,
            kind: entry.kind,
        })
        .collect();

    CommandsPayload {
        schema_version: "roswire.commands.v1",
        commands,
    }
}

fn help_payload(tokens: &[String]) -> RosWireResult<String> {
    if tokens.len() == 1 {
        let payload = HelpIndexPayload {
            schema_version: "roswire.help.index.v1",
            global_options: vec![
                "--profile".to_owned(),
                "--host".to_owned(),
                "--user".to_owned(),
                "--password".to_owned(),
                "--protocol".to_owned(),
                "--routeros-version".to_owned(),
                "--transfer".to_owned(),
                "--port".to_owned(),
                "--json".to_owned(),
                "--debug".to_owned(),
                "--include-remote".to_owned(),
                "--refresh".to_owned(),
                "--source".to_owned(),
                "--allow-write".to_owned(),
            ],
            commands: commands_payload().commands,
        };
        return render_json(&payload);
    }

    let topic = normalize_topic(&tokens[1..]);
    let command = lookup_command(&topic)
        .ok_or_else(|| Box::new(RosWireError::help_topic_not_found(topic.clone())))?;

    render_json(&HelpPayload {
        schema_version: "roswire.command.help.v1",
        command,
    })
}

fn schema_payload(tokens: &[String]) -> RosWireResult<String> {
    if tokens.get(1).map(String::as_str) == Some("discover") {
        return Err(Box::new(RosWireError::usage(
            "schema discover requires --remote: roswire schema discover --remote --json",
        )));
    }

    if tokens.len() < 3 || tokens[1].as_str() != "command" {
        return Err(Box::new(RosWireError::usage(
            "schema command requires: roswire schema command <command...>",
        )));
    }

    let topic = normalize_topic(&tokens[2..]);
    let command = lookup_command(&topic)
        .ok_or_else(|| Box::new(RosWireError::schema_unavailable(topic.clone())))?;

    render_json(&SchemaPayload {
        schema_version: "roswire.command.schema.v1",
        command: command.name,
        arguments: command.arguments,
    })
}

fn remote_schema_payload(tokens: &[String], cli: &Cli) -> RosWireResult<String> {
    let commands = catalog();
    let policies = match tokens.get(1).map(String::as_str) {
        Some("discover") => discovery::policies_from_catalog(&commands),
        Some("command") if tokens.len() >= 3 => {
            let topic = normalize_topic(&tokens[2..]);
            let command = lookup_command(&topic)
                .ok_or_else(|| Box::new(RosWireError::schema_unavailable(topic.clone())))?;
            discovery::policy_from_command(&command)
                .map(|policy| vec![policy])
                .ok_or_else(|| Box::new(RosWireError::schema_unavailable(topic)))?
        }
        _ => {
            return Err(Box::new(RosWireError::usage(
                "remote schema requires: roswire schema discover --remote --json or roswire schema command <command...> --remote --json",
            )));
        }
    };

    let (profile, fingerprint, warning) = match crate::resolve_execution_target(cli) {
        Ok(target) => (
            cli.profile.clone().unwrap_or_else(|| "default".to_owned()),
            discovery::unknown_fingerprint(&target.host, &target.requested_protocol),
            "CAPABILITY_PROBE_FAILED".to_owned(),
        ),
        Err(error) => (
            cli.profile.clone().unwrap_or_else(|| "default".to_owned()),
            discovery::unknown_fingerprint("unknown", "unknown"),
            discovery::warning_name(error.error_code),
        ),
    };

    let cache_status = if cli.refresh {
        cache::CacheLookupStatus::Refresh
    } else {
        cache::CacheLookupStatus::Miss
    };
    let snapshot = discovery::degraded_remote_schema_snapshot_with_cache_status(
        &profile,
        &fingerprint,
        policies,
        warning,
        cache_status,
    );
    render_json(&snapshot)
}

fn explain_error_payload(tokens: &[String]) -> RosWireResult<String> {
    if tokens.len() < 2 {
        return Err(Box::new(RosWireError::usage(
            "missing error code: roswire explain-error <CODE>",
        )));
    }

    let code = tokens[1].to_ascii_uppercase();
    let payload = match code.as_str() {
        "ROS_API_FAILURE" => ExplainErrorPayload {
            schema_version: "roswire.error.explain.v1",
            error_code: code,
            summary: "RouterOS returned a trap or command failure.".to_owned(),
            common_causes: vec![
                "target interface/item does not exist".to_owned(),
                "argument value does not match menu expectations".to_owned(),
            ],
            suggested_next_steps: vec![
                "run `roswire interface print --json` to discover valid interfaces".to_owned(),
                "verify key=value arguments and .id references".to_owned(),
            ],
        },
        "USAGE_ERROR" => ExplainErrorPayload {
            schema_version: "roswire.error.explain.v1",
            error_code: code,
            summary: "CLI arguments are missing or invalid.".to_owned(),
            common_causes: vec![
                "missing action token".to_owned(),
                "malformed key=value argument".to_owned(),
            ],
            suggested_next_steps: vec![
                "run `roswire help --json` to inspect command contracts".to_owned(),
                "run `roswire commands --json` for command discovery".to_owned(),
            ],
        },
        "SSH_RESTORE_FAILED" => ExplainErrorPayload {
            schema_version: "roswire.error.explain.v1",
            error_code: code,
            summary:
                "RosWire could not restore the RouterOS SSH service state captured before transfer."
                    .to_owned(),
            common_causes: vec![
                "RouterOS rejected /ip service ssh restore fields".to_owned(),
                "control protocol connection failed during cleanup".to_owned(),
            ],
            suggested_next_steps: vec![
                "inspect `/ip service print where name=ssh` on the router".to_owned(),
                "restore the intended disabled/address values manually before retrying".to_owned(),
            ],
        },
        _ => {
            return Err(Box::new(RosWireError::help_topic_not_found(format!(
                "error code {code}",
            ))));
        }
    };

    render_json(&payload)
}

fn render_json<T: Serialize>(value: &T) -> RosWireResult<String> {
    serde_json::to_string_pretty(value).map_err(|error| {
        Box::new(RosWireError::internal(format!(
            "failed to serialize introspection payload: {error}",
        )))
    })
}

fn normalize_topic(tokens: &[String]) -> String {
    tokens
        .iter()
        .flat_map(|token| token.split_whitespace())
        .collect::<Vec<_>>()
        .join(" ")
        .to_ascii_lowercase()
}

fn lookup_command(topic: &str) -> Option<CommandDefinition> {
    catalog()
        .into_iter()
        .find(|command| command.name.eq_ignore_ascii_case(topic))
}

fn print_option_argument(name: &str, description: &str) -> ArgumentSpec {
    ArgumentSpec {
        name: name.to_owned(),
        style: "flag".to_owned(),
        required: false,
        arg_type: "readonly-print-option".to_owned(),
        description: description.to_owned(),
        example: Some(name.to_owned()),
    }
}

fn catalog() -> Vec<CommandDefinition> {
    vec![
        CommandDefinition {
            name: "ip address print".to_owned(),
            summary: "Print IP address records.".to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire ip address print --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire ip address print --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "ip address add".to_owned(),
            summary: "Add an IP address entry.".to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire ip address add address=<cidr> interface=<name> --json".to_owned(),
            arguments: vec![
                ArgumentSpec {
                    name: "address".to_owned(),
                    style: "key-value".to_owned(),
                    required: true,
                    arg_type: "cidr".to_owned(),
                    description: "Address with prefix length.".to_owned(),
                    example: Some("192.168.88.2/24".to_owned()),
                },
                ArgumentSpec {
                    name: "interface".to_owned(),
                    style: "key-value".to_owned(),
                    required: true,
                    arg_type: "string".to_owned(),
                    description: "RouterOS interface name.".to_owned(),
                    example: Some("bridge".to_owned()),
                },
            ],
            examples: vec![
                "roswire ip address add address=192.168.88.2/24 interface=bridge --json".to_owned(),
            ],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "ip address set".to_owned(),
            summary: "Update an existing IP address entry.".to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire ip address set .id=<id> disabled=<bool> --json".to_owned(),
            arguments: vec![
                ArgumentSpec {
                    name: ".id".to_owned(),
                    style: "key-value".to_owned(),
                    required: true,
                    arg_type: "string".to_owned(),
                    description: "RouterOS internal item ID.".to_owned(),
                    example: Some("*1".to_owned()),
                },
                ArgumentSpec {
                    name: "disabled".to_owned(),
                    style: "key-value".to_owned(),
                    required: false,
                    arg_type: "bool".to_owned(),
                    description: "Disable or enable the address entry.".to_owned(),
                    example: Some("true".to_owned()),
                },
            ],
            examples: vec!["roswire ip address set .id=*1 disabled=true --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "ip address remove".to_owned(),
            summary: "Remove an IP address entry.".to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire ip address remove .id=<id> --json".to_owned(),
            arguments: vec![ArgumentSpec {
                name: ".id".to_owned(),
                style: "key-value".to_owned(),
                required: true,
                arg_type: "string".to_owned(),
                description: "RouterOS internal item ID.".to_owned(),
                example: Some("*1".to_owned()),
            }],
            examples: vec!["roswire ip address remove .id=*1 --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "ip dhcp-client print".to_owned(),
            summary: "Print DHCP client records; accepts the read-only `detail` print option."
                .to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire ip dhcp-client print [detail] --json".to_owned(),
            arguments: vec![print_option_argument(
                "detail",
                "Return detailed property=value output for DHCP client records.",
            )],
            examples: vec![
                "roswire ip dhcp-client print --json".to_owned(),
                "roswire ip dhcp-client print detail --json".to_owned(),
            ],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "ip firewall address-list print".to_owned(),
            summary: "Print firewall address-list entries.".to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire ip firewall address-list print --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire ip firewall address-list print --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "ip firewall filter print".to_owned(),
            summary: "Print firewall filter rules without changing packet handling.".to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire ip firewall filter print [stats] --json".to_owned(),
            arguments: vec![print_option_argument(
                "stats",
                "Include read-only firewall counter statistics in the print response.",
            )],
            examples: vec![
                "roswire ip firewall filter print --json".to_owned(),
                "roswire ip firewall filter print stats --json".to_owned(),
            ],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "ip firewall nat print".to_owned(),
            summary: "Print firewall NAT rules without changing packet handling.".to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire ip firewall nat print [stats] --json".to_owned(),
            arguments: vec![print_option_argument(
                "stats",
                "Include read-only NAT counter statistics in the print response.",
            )],
            examples: vec![
                "roswire ip firewall nat print --json".to_owned(),
                "roswire ip firewall nat print stats --json".to_owned(),
            ],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "ip firewall connection print".to_owned(),
            summary: "Print firewall connection tracking entries; accepts read-only `count-only`."
                .to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire ip firewall connection print [count-only] --json".to_owned(),
            arguments: vec![print_option_argument(
                "count-only",
                "Return only the number of matching connection tracking entries.",
            )],
            examples: vec![
                "roswire ip firewall connection print --json".to_owned(),
                "roswire ip firewall connection print count-only --json".to_owned(),
            ],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "ip route print".to_owned(),
            summary: "Print RouterOS IP routes, including v6/v7 route table fields when present."
                .to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire ip route print --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire ip route print --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "interface print".to_owned(),
            summary: "Print interface list.".to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire interface print --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire interface print --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "interface wireguard print".to_owned(),
            summary: "Print RouterOS v7 WireGuard interfaces without exposing private keys."
                .to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire interface wireguard print --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire interface wireguard print --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "interface wireguard peers print".to_owned(),
            summary: "Print RouterOS v7 WireGuard peers without exposing preshared keys."
                .to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire interface wireguard peers print --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire interface wireguard peers print --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "system package print".to_owned(),
            summary: "Print installed RouterOS packages.".to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire system package print --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire system package print --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "script put".to_owned(),
            summary: "Read local .rsc text and store it as a RouterOS system script without creating a RouterOS file.".to_owned(),
            kind: "workflow".to_owned(),
            syntax: "roswire script put <name> --source @<local.rsc> --json".to_owned(),
            arguments: vec![
                ArgumentSpec {
                    name: "name".to_owned(),
                    style: "positional".to_owned(),
                    required: true,
                    arg_type: "string".to_owned(),
                    description: "RouterOS system script name to create.".to_owned(),
                    example: Some("bootstrap".to_owned()),
                },
                ArgumentSpec {
                    name: "--source".to_owned(),
                    style: "option".to_owned(),
                    required: true,
                    arg_type: "@path".to_owned(),
                    description: "Local UTF-8 .rsc file to read as script source. The content is never printed in errors or dry-run output.".to_owned(),
                    example: Some("@setup.rsc".to_owned()),
                },
            ],
            examples: vec![
                "roswire script put bootstrap --source @setup.rsc --dry-run --json".to_owned(),
                "roswire --profile lab script put bootstrap --source @setup.rsc --json".to_owned(),
            ],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "FILE_TOO_LARGE".to_owned(),
                "CONFIG_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "raw".to_owned(),
            summary: "Explicitly pass a RouterOS API path plus read-only print options or key=value words for advanced unsupported commands.".to_owned(),
            kind: "raw-routeros-command".to_owned(),
            syntax: "roswire raw /system/resource/print [detail|stats|count-only] [key=value ...] --json".to_owned(),
            arguments: vec![
                ArgumentSpec {
                    name: "routeros-path".to_owned(),
                    style: "positional".to_owned(),
                    required: true,
                    arg_type: "classic-api-path".to_owned(),
                    description: "RouterOS classic API path beginning with `/`, e.g. /system/resource/print.".to_owned(),
                    example: Some("/system/resource/print".to_owned()),
                },
                ArgumentSpec {
                    name: "key=value".to_owned(),
                    style: "key-value".to_owned(),
                    required: false,
                    arg_type: "string".to_owned(),
                    description: "Additional RouterOS API word arguments. Sensitive keys and local paths are redacted in errors/logs.".to_owned(),
                    example: Some("detail=yes".to_owned()),
                },
                print_option_argument(
                    "detail|stats|count-only",
                    "Accepted bare options for raw read-only `/.../print` commands. Unsafe options such as file, interval, follow, and unknown bare tokens are rejected.",
                ),
                ArgumentSpec {
                    name: "--allow-write".to_owned(),
                    style: "flag".to_owned(),
                    required: false,
                    arg_type: "bool".to_owned(),
                    description: "Required for raw commands that are not `/.../print`; read-only raw print can use classic API or REST POST.".to_owned(),
                    example: Some("--allow-write".to_owned()),
                },
            ],
            examples: vec![
                "roswire raw /system/resource/print --json".to_owned(),
                "roswire raw /ip/dhcp-client/print detail --json".to_owned(),
                "roswire raw /ip/firewall/connection/print count-only --json".to_owned(),
                "roswire raw /tool/fetch url=https://example.invalid/a.rsc --allow-write --json".to_owned(),
            ],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "CONFIG_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
                "UNSUPPORTED_ACTION".to_owned(),
            ],
        },
        CommandDefinition {
            name: "tool mac-server print".to_owned(),
            summary: "Print RouterOS MAC server settings without changing service state."
                .to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire tool mac-server print --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire tool mac-server print --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "tool netwatch print".to_owned(),
            summary: "Print RouterOS Netwatch entries; does not run ad-hoc probes.".to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire tool netwatch print --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire tool netwatch print --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "user print".to_owned(),
            summary: "Print RouterOS users without exposing password material.".to_owned(),
            kind: "routeros-command".to_owned(),
            syntax: "roswire user print --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire user print --json".to_owned()],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "config inspect".to_owned(),
            summary: "Inspect resolved local configuration and source precedence.".to_owned(),
            kind: "config".to_owned(),
            syntax: "roswire config inspect --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire config inspect --json".to_owned()],
            errors: vec![
                "CONFIG_ERROR".to_owned(),
                "PROFILE_NOT_FOUND".to_owned(),
                "CONFIG_INSECURE_PERMISSIONS".to_owned(),
            ],
        },
        CommandDefinition {
            name: "config init".to_owned(),
            summary: "Initialize ~/.roswire home, logs, and config.toml".to_owned(),
            kind: "config".to_owned(),
            syntax: "roswire config init --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire config init --json".to_owned()],
            errors: vec!["CONFIG_ERROR".to_owned(), "INTERNAL_ERROR".to_owned()],
        },
        CommandDefinition {
            name: "config profiles".to_owned(),
            summary: "List configured profiles and default profile.".to_owned(),
            kind: "config".to_owned(),
            syntax: "roswire config profiles --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire config profiles --json".to_owned()],
            errors: vec!["CONFIG_ERROR".to_owned()],
        },
        CommandDefinition {
            name: "config device add".to_owned(),
            summary: "Create a profile/device entry in config.toml.".to_owned(),
            kind: "config".to_owned(),
            syntax: "roswire config device add <profile> host=<host> user=<user> [protocol=<mode>] [transfer=<mode>] [port=<n>] [ssh_host_key=<fingerprint>] [allow_from=<cidr>[,<cidr>...]] --json".to_owned(),
            arguments: vec![
                ArgumentSpec {
                    name: "profile".to_owned(),
                    style: "positional".to_owned(),
                    required: true,
                    arg_type: "string".to_owned(),
                    description: "Profile name to create.".to_owned(),
                    example: Some("studio-router".to_owned()),
                },
                ArgumentSpec {
                    name: "host".to_owned(),
                    style: "key-value".to_owned(),
                    required: true,
                    arg_type: "string".to_owned(),
                    description: "Router host/IP".to_owned(),
                    example: Some("10.189.189.1".to_owned()),
                },
                ArgumentSpec {
                    name: "user".to_owned(),
                    style: "key-value".to_owned(),
                    required: true,
                    arg_type: "string".to_owned(),
                    description: "Router username".to_owned(),
                    example: Some("master".to_owned()),
                },
            ],
            examples: vec![
                "roswire config device add studio-router host=10.189.189.1 user=master ssh_host_key=SHA256:abc allow_from=203.0.113.10/32 --json".to_owned(),
            ],
            errors: vec!["USAGE_ERROR".to_owned(), "CONFIG_ERROR".to_owned()],
        },
        CommandDefinition {
            name: "config device set".to_owned(),
            summary: "Update an existing profile/device entry in config.toml.".to_owned(),
            kind: "config".to_owned(),
            syntax: "roswire config device set <profile> [host=<host>] [user=<user>] [protocol=<mode>] [transfer=<mode>] [port=<n>] [ssh_host_key=<fingerprint>] [allow_from=<cidr>[,<cidr>...]] --json".to_owned(),
            arguments: vec![ArgumentSpec {
                name: "profile".to_owned(),
                style: "positional".to_owned(),
                required: true,
                arg_type: "string".to_owned(),
                description: "Profile name to update.".to_owned(),
                example: Some("studio-router".to_owned()),
            }],
            examples: vec![
                "roswire config device set studio-router protocol=rest --json".to_owned(),
            ],
            errors: vec!["USAGE_ERROR".to_owned(), "CONFIG_ERROR".to_owned()],
        },
        CommandDefinition {
            name: "config secret set".to_owned(),
            summary: "Set profile secret using plain/encrypted/keychain/same-as types.".to_owned(),
            kind: "config".to_owned(),
            syntax: "roswire config secret set <profile> <name> type=<plain|encrypted|keychain|same-as> ... --json".to_owned(),
            arguments: vec![
                ArgumentSpec {
                    name: "profile".to_owned(),
                    style: "positional".to_owned(),
                    required: true,
                    arg_type: "string".to_owned(),
                    description: "Profile name.".to_owned(),
                    example: Some("studio-router".to_owned()),
                },
                ArgumentSpec {
                    name: "name".to_owned(),
                    style: "positional".to_owned(),
                    required: true,
                    arg_type: "string".to_owned(),
                    description: "Secret key name, e.g. password.".to_owned(),
                    example: Some("password".to_owned()),
                },
                ArgumentSpec {
                    name: "type".to_owned(),
                    style: "key-value".to_owned(),
                    required: true,
                    arg_type: "string".to_owned(),
                    description: "Secret backend type.".to_owned(),
                    example: Some("keychain".to_owned()),
                },
            ],
            examples: vec![
                "roswire config secret set studio-router password type=plain value=All.007! --json".to_owned(),
                "roswire config secret set studio-router password type=keychain service=roswire account=profiles/studio-router/password --json".to_owned(),
                "roswire config secret set studio-router ssh_password type=same-as target=password --json".to_owned(),
                "roswire config secret set studio-router ssh_key_passphrase type=env var=ROSWIRE_STUDIO_SSH_KEY_PASSPHRASE --json".to_owned(),
            ],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "PROFILE_NOT_FOUND".to_owned(),
                "CONFIG_ERROR".to_owned(),
            ],
        },
        CommandDefinition {
            name: "doctor".to_owned(),
            summary: "Run local diagnostics and optionally include read-only remote checks."
                .to_owned(),
            kind: "introspection".to_owned(),
            syntax: "roswire doctor [--include-remote] --json".to_owned(),
            arguments: vec![ArgumentSpec {
                name: "--include-remote".to_owned(),
                style: "flag".to_owned(),
                required: false,
                arg_type: "bool".to_owned(),
                description: "Include read-only RouterOS port/login/resource diagnostics."
                    .to_owned(),
                example: Some("--include-remote".to_owned()),
            }],
            examples: vec![
                "roswire doctor --json".to_owned(),
                "roswire --profile studio-router doctor --include-remote --json".to_owned(),
            ],
            errors: vec![
                "CONFIG_ERROR".to_owned(),
                "AUTH_FAILED".to_owned(),
                "NETWORK_ERROR".to_owned(),
                "ROS_API_FAILURE".to_owned(),
            ],
        },
        CommandDefinition {
            name: "commands".to_owned(),
            summary: "List command index for agent discovery.".to_owned(),
            kind: "introspection".to_owned(),
            syntax: "roswire commands --json".to_owned(),
            arguments: vec![],
            examples: vec!["roswire commands --json".to_owned()],
            errors: vec!["USAGE_ERROR".to_owned()],
        },
        CommandDefinition {
            name: "schema command".to_owned(),
            summary: "Show argument schema for a command topic.".to_owned(),
            kind: "introspection".to_owned(),
            syntax: "roswire schema command ip address add --json".to_owned(),
            arguments: vec![ArgumentSpec {
                name: "command".to_owned(),
                style: "positional".to_owned(),
                required: true,
                arg_type: "string".to_owned(),
                description: "Command topic, e.g. `ip address add`.".to_owned(),
                example: Some("ip address add".to_owned()),
            }],
            examples: vec!["roswire schema command ip address add --json".to_owned()],
            errors: vec!["SCHEMA_UNAVAILABLE".to_owned(), "USAGE_ERROR".to_owned()],
        },
        CommandDefinition {
            name: "schema discover".to_owned(),
            summary: "Discover remote schema overlay with cache TTL metadata.".to_owned(),
            kind: "introspection".to_owned(),
            syntax: "roswire schema discover --remote [--refresh] --json".to_owned(),
            arguments: vec![
                ArgumentSpec {
                    name: "--remote".to_owned(),
                    style: "flag".to_owned(),
                    required: true,
                    arg_type: "bool".to_owned(),
                    description: "Enable remote schema discovery.".to_owned(),
                    example: Some("--remote".to_owned()),
                },
                ArgumentSpec {
                    name: "--refresh".to_owned(),
                    style: "flag".to_owned(),
                    required: false,
                    arg_type: "bool".to_owned(),
                    description: "Bypass cached remote schema metadata and mark the cache status as refresh.".to_owned(),
                    example: Some("--refresh".to_owned()),
                },
            ],
            examples: vec![
                "roswire schema discover --remote --json".to_owned(),
                "roswire schema discover --remote --refresh --json".to_owned(),
            ],
            errors: vec![
                "USAGE_ERROR".to_owned(),
                "CONFIG_ERROR".to_owned(),
                "REMOTE_SCHEMA_UNAVAILABLE".to_owned(),
            ],
        },
    ]
}
