//! Generic security and code quality DETECTION rules
//!
//! These rules apply across multiple languages for static analysis.

use crate::rules::{Rule, create_finding, create_finding_at_line};
use regex::Regex;
use rma_common::{Finding, Language, Severity};
use rma_parser::ParsedFile;
use std::sync::LazyLock;
use tree_sitter::Node;

// Regex patterns for security checks
//
// IMPORTANT: These patterns are designed to minimize false positives.
// We only match SPECIFIC secret patterns, not generic "key" or "token" words.

/// Matches specific secret variable assignments like `api_key = "..."` or `password: "..."`
/// Note: Requires the variable name to be a COMPOUND secret name (api_key, secret_key, etc.)
/// NOT just "key" or "token" alone which are too generic.
static SECRET_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(r#"(?i)\b(api[_-]?key|secret[_-]?key|auth[_-]?token|access[_-]?token|private[_-]?key|access[_-]?key|client[_-]?secret|db[_-]?password|database[_-]?password|admin[_-]?password)\s*[:=]\s*["'][^"']{8,}["']"#).unwrap()
});

/// Matches AWS access key IDs (always start with AKIA)
static AWS_KEY_PATTERN: LazyLock<Regex> =
    LazyLock::new(|| Regex::new(r#"AKIA[0-9A-Z]{16}"#).unwrap());

/// Matches AWS secret access keys with the variable name
static AWS_SECRET_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(r#"(?i)aws[_-]?secret[_-]?access[_-]?key\s*[:=]\s*["'][A-Za-z0-9/+=]{40}["']"#)
        .unwrap()
});

/// Matches GitHub personal access tokens (ghp_) and GitHub app tokens (ghs_)
static GITHUB_TOKEN_PATTERN: LazyLock<Regex> =
    LazyLock::new(|| Regex::new(r#"gh[ps]_[A-Za-z0-9]{36,}"#).unwrap());

/// Matches PEM-encoded private keys
static PRIVATE_KEY_PATTERN: LazyLock<Regex> =
    LazyLock::new(|| Regex::new(r#"-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----"#).unwrap());

/// Matches password assignments with actual password values (not empty, not placeholders)
/// More restrictive: only `password` or `passwd` followed by a value that looks like a real password
static PASSWORD_ASSIGNMENT_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(r#"(?i)\b(password|passwd|pwd)\s*[:=]\s*["']([^"']{6,})["']"#).unwrap()
});

/// DETECTS TODO/FIXME comments that may indicate incomplete code
pub struct TodoFixmeRule;

impl Rule for TodoFixmeRule {
    fn id(&self) -> &str {
        "generic/todo-fixme"
    }

    fn description(&self) -> &str {
        "Detects TODO and FIXME comments that may indicate incomplete functionality"
    }

    fn applies_to(&self, _lang: Language) -> bool {
        true
    }

    fn check(&self, parsed: &ParsedFile) -> Vec<Finding> {
        let mut findings = Vec::new();

        for (line_num, line) in parsed.content.lines().enumerate() {
            let upper = line.to_uppercase();
            if upper.contains("TODO")
                || upper.contains("FIXME")
                || upper.contains("HACK")
                || upper.contains("XXX")
            {
                let mut finding = Finding {
                    id: format!("{}-{}", self.id(), line_num),
                    rule_id: self.id().to_string(),
                    message: "TODO/FIXME comment indicates potentially incomplete code".to_string(),
                    severity: Severity::Info,
                    location: rma_common::SourceLocation::new(
                        parsed.path.clone(),
                        line_num + 1,
                        1,
                        line_num + 1,
                        line.len(),
                    ),
                    language: parsed.language,
                    snippet: Some(line.trim().to_string()),
                    suggestion: None,
                    confidence: rma_common::Confidence::High,
                    category: rma_common::FindingCategory::Style,
                    fingerprint: None,
                };
                finding.compute_fingerprint();
                findings.push(finding);
            }
        }
        findings
    }
}

/// DETECTS functions that exceed a line count threshold
pub struct LongFunctionRule {
    max_lines: usize,
}

impl LongFunctionRule {
    pub fn new(max_lines: usize) -> Self {
        Self { max_lines }
    }
}

impl Rule for LongFunctionRule {
    fn id(&self) -> &str {
        "generic/long-function"
    }

    fn description(&self) -> &str {
        "Detects functions that exceed the recommended line count"
    }

    fn applies_to(&self, _lang: Language) -> bool {
        true
    }

    fn check(&self, parsed: &ParsedFile) -> Vec<Finding> {
        let mut findings = Vec::new();
        let mut cursor = parsed.tree.walk();

        let function_kinds = [
            "function_item",
            "function_declaration",
            "function_definition",
            "method_declaration",
            "arrow_function",
        ];

        find_nodes_by_kinds(&mut cursor, &function_kinds, |node: Node| {
            let start = node.start_position().row;
            let end = node.end_position().row;
            let lines = end - start + 1;

            if lines > self.max_lines {
                findings.push(create_finding(
                    self.id(),
                    &node,
                    &parsed.path,
                    &parsed.content,
                    Severity::Warning,
                    &format!(
                        "Function has {} lines (max: {}) - consider refactoring",
                        lines, self.max_lines
                    ),
                    parsed.language,
                ));
            }
        });
        findings
    }
}

/// DETECTS high cyclomatic complexity
pub struct HighComplexityRule {
    max_complexity: usize,
}

impl HighComplexityRule {
    pub fn new(max_complexity: usize) -> Self {
        Self { max_complexity }
    }
}

/// DETECTS duplicate functions (copy-paste code)
pub struct DuplicateFunctionRule {
    min_lines: usize,
}

impl DuplicateFunctionRule {
    pub fn new(min_lines: usize) -> Self {
        Self { min_lines }
    }

    /// Extract and normalize just the function body (inside braces) for comparison
    fn normalize_body(content: &str, node: &Node) -> String {
        // Find the block/body child node (the part inside braces)
        let mut cursor = node.walk();
        let mut body_node: Option<Node> = None;

        if cursor.goto_first_child() {
            loop {
                let child = cursor.node();
                // Look for block-like nodes that contain the function body
                if child.kind() == "block"
                    || child.kind() == "statement_block"
                    || child.kind() == "compound_statement"
                    || child.kind() == "function_body"
                {
                    body_node = Some(child);
                    break;
                }
                if !cursor.goto_next_sibling() {
                    break;
                }
            }
        }

        let body = if let Some(bn) = body_node {
            let start = bn.start_byte();
            let end = bn.end_byte();
            if end <= content.len() && start < end {
                &content[start..end]
            } else {
                return String::new();
            }
        } else {
            // Fallback: use entire node but try to skip signature
            let start = node.start_byte();
            let end = node.end_byte();
            if end > content.len() || start >= end {
                return String::new();
            }
            &content[start..end]
        };

        // Normalize: remove whitespace, lowercase, strip comments
        let mut result = String::new();
        let mut in_line_comment = false;
        let mut in_block_comment = false;
        let mut prev_char = ' ';

        for c in body.chars() {
            if in_line_comment {
                if c == '\n' {
                    in_line_comment = false;
                }
                continue;
            }
            if in_block_comment {
                if prev_char == '*' && c == '/' {
                    in_block_comment = false;
                }
                prev_char = c;
                continue;
            }
            if prev_char == '/' && c == '/' {
                in_line_comment = true;
                result.pop(); // remove the first /
                continue;
            }
            if prev_char == '/' && c == '*' {
                in_block_comment = true;
                result.pop(); // remove the first /
                continue;
            }
            if !c.is_whitespace() {
                result.push(c.to_ascii_lowercase());
            }
            prev_char = c;
        }

        result
    }

    /// Get function name from node
    fn get_function_name(node: &Node, content: &str) -> Option<String> {
        let mut cursor = node.walk();
        if cursor.goto_first_child() {
            loop {
                let child = cursor.node();
                if child.kind() == "identifier"
                    || child.kind() == "name"
                    || child.kind() == "property_identifier"
                {
                    let start = child.start_byte();
                    let end = child.end_byte();
                    if end <= content.len() {
                        return Some(content[start..end].to_string());
                    }
                }
                if !cursor.goto_next_sibling() {
                    break;
                }
            }
        }
        None
    }
}

impl Rule for HighComplexityRule {
    fn id(&self) -> &str {
        "generic/high-complexity"
    }

    fn description(&self) -> &str {
        "Detects functions with high cyclomatic complexity"
    }

    fn applies_to(&self, _lang: Language) -> bool {
        true
    }

    fn check(&self, parsed: &ParsedFile) -> Vec<Finding> {
        let mut findings = Vec::new();
        let mut cursor = parsed.tree.walk();

        let function_kinds = [
            "function_item",
            "function_declaration",
            "function_definition",
            "method_declaration",
        ];

        find_nodes_by_kinds(&mut cursor, &function_kinds, |node: Node| {
            let complexity = count_branches(&node, parsed.language);

            if complexity > self.max_complexity {
                findings.push(create_finding(
                    self.id(),
                    &node,
                    &parsed.path,
                    &parsed.content,
                    Severity::Warning,
                    &format!(
                        "Function has complexity {} (max: {}) - consider simplifying",
                        complexity, self.max_complexity
                    ),
                    parsed.language,
                ));
            }
        });
        findings
    }
}

impl Rule for DuplicateFunctionRule {
    fn id(&self) -> &str {
        "generic/duplicate-function"
    }

    fn description(&self) -> &str {
        "Detects duplicate functions that could be refactored"
    }

    fn applies_to(&self, _lang: Language) -> bool {
        true
    }

    fn check(&self, parsed: &ParsedFile) -> Vec<Finding> {
        use std::collections::HashMap;

        let mut findings = Vec::new();
        let mut cursor = parsed.tree.walk();

        let function_kinds = [
            "function_item",
            "function_declaration",
            "function_definition",
            "method_declaration",
            "arrow_function",
        ];

        // Collect all functions with their normalized bodies
        struct FuncInfo {
            name: String,
            line: usize,
            col: usize,
        }

        let mut body_to_funcs: HashMap<String, Vec<FuncInfo>> = HashMap::new();

        find_nodes_by_kinds(&mut cursor, &function_kinds, |node: Node| {
            let start = node.start_position().row;
            let end = node.end_position().row;
            let lines = end - start + 1;

            // Only check functions above minimum line threshold
            if lines < self.min_lines {
                return;
            }

            let normalized = Self::normalize_body(&parsed.content, &node);
            if normalized.len() < 50 {
                // Skip very small functions
                return;
            }

            let name = Self::get_function_name(&node, &parsed.content)
                .unwrap_or_else(|| format!("anonymous@{}", start + 1));

            body_to_funcs.entry(normalized).or_default().push(FuncInfo {
                name,
                line: start + 1,
                col: node.start_position().column + 1,
            });
        });

        // Report duplicates
        for (_body, funcs) in body_to_funcs.iter() {
            if funcs.len() > 1 {
                // Report all but the first as duplicates
                let first = &funcs[0];
                for dup in funcs.iter().skip(1) {
                    let mut finding = Finding {
                        id: format!("{}-{}-{}", self.id(), dup.line, dup.col),
                        rule_id: self.id().to_string(),
                        message: format!(
                            "Function '{}' is a duplicate of '{}' at line {} - consider extracting to shared function",
                            dup.name, first.name, first.line
                        ),
                        severity: Severity::Warning,
                        location: rma_common::SourceLocation::new(
                            parsed.path.clone(),
                            dup.line,
                            dup.col,
                            dup.line,
                            dup.col + 10,
                        ),
                        language: parsed.language,
                        snippet: Some(format!("fn {}(...)", dup.name)),
                        suggestion: Some(format!(
                            "Extract shared logic from '{}' and '{}'",
                            first.name, dup.name
                        )),
                        confidence: rma_common::Confidence::High,
                        category: rma_common::FindingCategory::Style,
                        fingerprint: None,
                    };
                    finding.compute_fingerprint();
                    findings.push(finding);
                }
            }
        }

        findings
    }
}

/// DETECTS hardcoded secrets, API keys, and passwords in any language
///
/// This rule focuses on HIGH-CONFIDENCE detection to minimize false positives.
/// It looks for:
/// - Specific secret patterns (api_key, secret_key, auth_token, etc.)
/// - Known credential formats (AWS keys, GitHub tokens, private keys)
/// - Password assignments with actual values
///
/// It does NOT flag:
/// - Generic "key" or "token" variable names (too many false positives)
/// - Object property keys (accessorKey, storageKey, etc.)
/// - HTTP header names
/// - Configuration constants that aren't secrets
pub struct HardcodedSecretRule;

impl HardcodedSecretRule {
    /// Check if a password value looks like a real password (not a placeholder)
    fn is_real_password(value: &str) -> bool {
        // Skip obvious placeholders and test values
        let lower = value.to_lowercase();
        if lower.is_empty()
            || lower == "password"
            || lower == "changeme"
            || lower == "placeholder"
            || lower == "your_password"
            || lower == "your-password"
            || lower == "xxx"
            || lower == "***"
            || lower.starts_with("${")
            || lower.starts_with("{{")
            || lower.contains("example")
            || lower.contains("test")
            || lower.contains("dummy")
            || lower.contains("sample")
            || lower.contains("fake")
            || lower.contains("mock")
        {
            return false;
        }

        // A real password typically has mixed characters or is long enough
        // to suggest it's not just a simple word
        let has_digit = value.chars().any(|c| c.is_ascii_digit());
        let has_upper = value.chars().any(|c| c.is_ascii_uppercase());
        let has_lower = value.chars().any(|c| c.is_ascii_lowercase());
        let has_special = value.chars().any(|c| !c.is_alphanumeric());

        // Strong signal: mixed case + digits + special chars
        // Or: long enough to be suspicious
        (has_digit && has_upper && has_lower)
            || (has_special && value.len() >= 8)
            || value.len() >= 16
    }

    /// Check if a line is a false positive context (object properties, configs, etc.)
    fn is_false_positive_context(line: &str) -> bool {
        let lower = line.to_lowercase();

        // Skip lines that are clearly not secrets
        // Object/struct property definitions
        if lower.contains("accessorkey")
            || lower.contains("storagekey")
            || lower.contains("cachekey")
            || lower.contains("localstoragekey")
            || lower.contains("sessionkey")
            || lower.contains("sortkey")
            || lower.contains("primarykey")
            || lower.contains("foreignkey")
            || lower.contains("uniquekey")
            || lower.contains("indexkey")
        {
            return true;
        }

        // HTTP headers and common config keys
        if lower.contains("cache-control")
            || lower.contains("content-type")
            || lower.contains("accept")
            || lower.contains("authorization: bearer")  // the header name, not value
            || lower.contains("x-api-key")
        // header name
        {
            return true;
        }

        // React/Vue/Angular component props and table columns
        if lower.contains("accessor:")
            || lower.contains("header:")
            || lower.contains("field:")
            || lower.contains("dataindex:")
        {
            return true;
        }

        // Translation keys, i18n
        if lower.contains("t('") || lower.contains("i18n") || lower.contains("translate") {
            return true;
        }

        // Type definitions and interfaces (TypeScript)
        if lower.contains(": string") || lower.contains(": number") || lower.contains("interface ")
        {
            return true;
        }

        false
    }
}

impl Rule for HardcodedSecretRule {
    fn id(&self) -> &str {
        "generic/hardcoded-secret"
    }

    fn description(&self) -> &str {
        "Detects hardcoded secrets, API keys, and passwords"
    }

    fn applies_to(&self, _lang: Language) -> bool {
        true
    }

    fn check(&self, parsed: &ParsedFile) -> Vec<Finding> {
        let mut findings = Vec::new();

        for (line_num, line) in parsed.content.lines().enumerate() {
            let trimmed = line.trim();

            // Skip comments in various languages
            if trimmed.starts_with("//")
                || trimmed.starts_with('#')
                || trimmed.starts_with("/*")
                || trimmed.starts_with('*')
                || trimmed.starts_with("'''")
                || trimmed.starts_with("\"\"\"")
            {
                continue;
            }

            // Skip false positive contexts
            if Self::is_false_positive_context(line) {
                continue;
            }

            // HIGH CONFIDENCE: Specific secret patterns (api_key, secret_key, auth_token, etc.)
            if SECRET_PATTERN.is_match(line) {
                findings.push(create_finding_at_line(
                    self.id(),
                    &parsed.path,
                    line_num + 1,
                    "[REDACTED SECRET]",
                    Severity::Critical,
                    "Hardcoded secret detected - use environment variables or a secrets manager",
                    parsed.language,
                ));
                continue;
            }

            // HIGH CONFIDENCE: AWS access keys (distinctive AKIA prefix)
            if AWS_KEY_PATTERN.is_match(line) {
                findings.push(create_finding_at_line(
                    self.id(),
                    &parsed.path,
                    line_num + 1,
                    "[REDACTED AWS KEY]",
                    Severity::Critical,
                    "AWS access key ID detected - never commit credentials",
                    parsed.language,
                ));
                continue;
            }

            // HIGH CONFIDENCE: AWS secret access keys
            if AWS_SECRET_PATTERN.is_match(line) {
                findings.push(create_finding_at_line(
                    self.id(),
                    &parsed.path,
                    line_num + 1,
                    "[REDACTED AWS SECRET]",
                    Severity::Critical,
                    "AWS secret access key detected - never commit credentials",
                    parsed.language,
                ));
                continue;
            }

            // HIGH CONFIDENCE: GitHub tokens (distinctive ghp_/ghs_ prefix)
            if GITHUB_TOKEN_PATTERN.is_match(line) {
                findings.push(create_finding_at_line(
                    self.id(),
                    &parsed.path,
                    line_num + 1,
                    "[REDACTED GITHUB TOKEN]",
                    Severity::Critical,
                    "GitHub token detected - use GITHUB_TOKEN secret instead",
                    parsed.language,
                ));
                continue;
            }

            // HIGH CONFIDENCE: PEM-encoded private keys
            if PRIVATE_KEY_PATTERN.is_match(line) {
                findings.push(create_finding_at_line(
                    self.id(),
                    &parsed.path,
                    line_num + 1,
                    "[REDACTED PRIVATE KEY]",
                    Severity::Critical,
                    "Private key detected in source - store in secure key management",
                    parsed.language,
                ));
                continue;
            }

            // MEDIUM CONFIDENCE: Password assignments with real-looking values
            if let Some(caps) = PASSWORD_ASSIGNMENT_PATTERN.captures(line)
                && let Some(value_match) = caps.get(2)
            {
                let value = value_match.as_str();
                if Self::is_real_password(value) {
                    findings.push(create_finding_at_line(
                        self.id(),
                        &parsed.path,
                        line_num + 1,
                        "[REDACTED PASSWORD]",
                        Severity::Critical,
                        "Hardcoded password detected - use environment variables or a secrets manager",
                        parsed.language,
                    ));
                }
            }
        }
        findings
    }
}

/// DETECTS use of weak cryptographic algorithms
pub struct InsecureCryptoRule;

impl Rule for InsecureCryptoRule {
    fn id(&self) -> &str {
        "generic/insecure-crypto"
    }

    fn description(&self) -> &str {
        "Detects use of weak or deprecated cryptographic algorithms"
    }

    fn applies_to(&self, _lang: Language) -> bool {
        true
    }

    fn check(&self, parsed: &ParsedFile) -> Vec<Finding> {
        let mut findings = Vec::new();

        for (line_num, line) in parsed.content.lines().enumerate() {
            let trimmed = line.trim();
            let lower = line.to_lowercase();

            // Skip comments (various languages)
            if trimmed.starts_with("//")
                || trimmed.starts_with("/*")
                || trimmed.starts_with('*')
                || trimmed.starts_with('#')
                || trimmed.starts_with("<!--")
            {
                continue;
            }

            // Skip detection code (lines that are checking for patterns)
            if lower.contains(".contains(")
                || lower.contains(".is_match(")
                || lower.contains("regex")
            {
                continue;
            }

            // Skip string literals that are error messages or documentation
            // (lines where the crypto term appears inside quotes for display)
            if is_in_string_literal(&lower, "md5")
                || is_in_string_literal(&lower, "sha1")
                || is_in_string_literal(&lower, "des")
                || is_in_string_literal(&lower, "rc4")
                || is_in_string_literal(&lower, "ecb")
            {
                continue;
            }

            // MD5 - broken for security use
            if lower.contains("md5")
                && (lower.contains("hash")
                    || lower.contains("digest")
                    || lower.contains("::")
                    || lower.contains("import")
                    || lower.contains("require"))
            {
                findings.push(create_finding_at_line(
                    self.id(),
                    &parsed.path,
                    line_num + 1,
                    line.trim(),
                    Severity::Error,
                    "MD5 is cryptographically broken - use SHA-256 or better for security",
                    parsed.language,
                ));
            }

            // SHA-1 - deprecated for security
            if lower.contains("sha1")
                && !lower.contains("sha1sum")
                && (lower.contains("hash")
                    || lower.contains("digest")
                    || lower.contains("::")
                    || lower.contains("import"))
            {
                findings.push(create_finding_at_line(
                    self.id(),
                    &parsed.path,
                    line_num + 1,
                    line.trim(),
                    Severity::Warning,
                    "SHA-1 is deprecated for security - use SHA-256 or better",
                    parsed.language,
                ));
            }

            // DES - broken
            if (lower.contains("des") || lower.contains("3des") || lower.contains("triple_des"))
                && (lower.contains("encrypt")
                    || lower.contains("cipher")
                    || lower.contains("crypto"))
            {
                findings.push(create_finding_at_line(
                    self.id(),
                    &parsed.path,
                    line_num + 1,
                    line.trim(),
                    Severity::Error,
                    "DES/3DES is insecure - use AES-256-GCM or ChaCha20-Poly1305",
                    parsed.language,
                ));
            }

            // RC4 - broken
            if lower.contains("rc4")
                && (lower.contains("cipher")
                    || lower.contains("crypto")
                    || lower.contains("encrypt"))
            {
                findings.push(create_finding_at_line(
                    self.id(),
                    &parsed.path,
                    line_num + 1,
                    line.trim(),
                    Severity::Critical,
                    "RC4 is completely broken - use AES-GCM or ChaCha20-Poly1305",
                    parsed.language,
                ));
            }

            // ECB mode - insecure
            if lower.contains("ecb")
                && (lower.contains("mode") || lower.contains("cipher") || lower.contains("aes"))
            {
                findings.push(create_finding_at_line(
                    self.id(),
                    &parsed.path,
                    line_num + 1,
                    line.trim(),
                    Severity::Error,
                    "ECB mode is insecure - use GCM, CBC with HMAC, or authenticated encryption",
                    parsed.language,
                ));
            }
        }
        findings
    }
}

/// Check if a term appears inside a string literal (for skipping error messages)
fn is_in_string_literal(line: &str, term: &str) -> bool {
    // Find the term and check if it's inside quotes
    if let Some(pos) = line.find(term) {
        let before = &line[..pos];
        // Count quotes before the term
        let double_quotes = before.matches('"').count();
        let single_quotes = before.matches('\'').count();
        // If odd number of quotes, we're inside a string
        double_quotes % 2 == 1 || single_quotes % 2 == 1
    } else {
        false
    }
}

fn find_nodes_by_kinds<F>(cursor: &mut tree_sitter::TreeCursor, kinds: &[&str], mut callback: F)
where
    F: FnMut(Node),
{
    loop {
        let node = cursor.node();
        if kinds.contains(&node.kind()) {
            callback(node);
        }
        if cursor.goto_first_child() {
            continue;
        }
        loop {
            if cursor.goto_next_sibling() {
                break;
            }
            if !cursor.goto_parent() {
                return;
            }
        }
    }
}

fn count_branches(node: &Node, lang: Language) -> usize {
    let branch_kinds: &[&str] = match lang {
        Language::Rust => &[
            "if_expression",
            "match_expression",
            "while_expression",
            "for_expression",
        ],
        Language::JavaScript | Language::TypeScript => &[
            "if_statement",
            "switch_statement",
            "for_statement",
            "while_statement",
        ],
        Language::Python => &[
            "if_statement",
            "for_statement",
            "while_statement",
            "try_statement",
        ],
        Language::Go => &["if_statement", "for_statement", "switch_statement"],
        Language::Java => &[
            "if_statement",
            "for_statement",
            "while_statement",
            "switch_expression",
        ],
        Language::Unknown => &[],
    };

    let mut count = 1;
    let mut cursor = node.walk();

    loop {
        let current = cursor.node();
        if branch_kinds.contains(&current.kind()) {
            count += 1;
        }
        if cursor.goto_first_child() {
            continue;
        }
        loop {
            if cursor.goto_next_sibling() {
                break;
            }
            if !cursor.goto_parent() {
                return count;
            }
        }
    }
}
