"""PATH-001: Path Traversal detection rule."""

from __future__ import annotations

import ast

from . import Rule
from ..models import Finding, Severity


class PathTraversalRule(Rule):
    """Detects potential path traversal vulnerabilities."""

    @property
    def rule_id(self) -> str:
        return "PATH-001"

    @property
    def name(self) -> str:
        return "Path Traversal"

    @property
    def severity(self) -> Severity:
        return Severity.HIGH

    @property
    def description(self) -> str:
        return "File operations without path validation may allow directory traversal."

    def suggest_fix(self, source: str, finding: Finding) -> str | None:
        """Suggest wrapping file open with resolve() + is_relative_to()."""
        lines = source.splitlines()
        idx = finding.line - 1
        if idx < 0 or idx >= len(lines):
            return None
        line = lines[idx]
        indent = line[: len(line) - len(line.lstrip())]
        return f'{indent}# TODO: Add path.resolve() and path.is_relative_to(BASE_DIR) check'

    def check(self, tree: ast.Module, source: str) -> list[Finding]:
        """Check for file operations on user-controlled paths without validation."""
        findings: list[Finding] = []
        target_funcs = ("open", "Path", "read_text", "read_bytes", "write_text")

        for node in ast.walk(tree):
            if not isinstance(node, ast.Call):
                continue
            func_name = self._get_func_name(node)
            if func_name not in target_funcs:
                continue

            parent = self._find_parent_function(tree, node)
            if not parent:
                continue

            body_src = ast.get_source_segment(source, parent) or ""
            if "resolve()" in body_src or "is_relative_to" in body_src:
                continue

            if ".." in ast.dump(node) or self._has_user_input(node):
                findings.append(Finding(
                    rule_id=self.rule_id,
                    severity=self.severity,
                    title="Potential Path Traversal",
                    description=f"Function {func_name}() without path validation.",
                    file="",
                    line=node.lineno,
                    code_snippet=self._get_snippet(source, node.lineno),
                    recommendation="Use path.resolve() and path.is_relative_to(base_dir).",
                    cwe="CWE-22",
                ))
        return findings

    def _has_user_input(self, node: ast.Call) -> bool:
        """Check if call arguments look like user-controlled input."""
        user_input_names = ("path", "file_path", "filename", "filepath", "url", "input")
        for arg in node.args:
            if isinstance(arg, ast.Name) and arg.id in user_input_names:
                return True
            if isinstance(arg, ast.Subscript):
                return True
        return False
