#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
# See LICENSE file for details.
"""
PULSE Worker Tools (Extended) — filesystem and search tools.

Split from the original combined file for Law 7 compliance.
Contains: tool_brain_query, tool_list_files, tool_search_code.

Memory/capture tools (tool_extract_knowledge_batch, tool_clean_intents_batch,
tool_check_antipattern, capture_to_bridge_log, save_session_json) live in
worker_tools_memory.py and are re-exported here so all existing callers keep
working with a single import path.
"""
from __future__ import annotations

import re
import subprocess
import sys
from pathlib import Path

SCRIPT_DIR = Path(__file__).resolve().parent
ROOT_DIR = SCRIPT_DIR.parent.parent
TOOLS_DIR = ROOT_DIR / "pulse" / "brain"
STATE_DIR = ROOT_DIR / "state"

ROOT_DIR_EXT = ROOT_DIR  # alias used by _guard_path


def _guard_path(path_str: str):
    """Ensure path stays within project root."""
    from pathlib import Path as _Path
    p = _Path(path_str).resolve()
    if not str(p).startswith(str(ROOT_DIR)):
        raise PermissionError(f'Path outside project root: {path_str}')
    return p


def tool_brain_query(query: str) -> str:
    try:
        result = subprocess.run(
            [sys.executable, str(TOOLS_DIR / "brain_query.py"), "--query", query, "--brief"],
            capture_output=True, text=True, timeout=30, cwd=str(ROOT_DIR),
            encoding="utf-8", errors="replace",
        )
        return result.stdout[:5000] if result.stdout else "No brain results."
    except Exception as e:
        return f"Brain query error: {e}"


def tool_list_files(pattern: str, path: str = ".") -> str:
    try:
        base = _guard_path(path)
    except PermissionError as e:
        return f"ERROR: {e}"
    if not base.exists():
        return f"ERROR: Path not found: {path}"
    matches = []
    try:
        for p in base.rglob(pattern):
            if p.is_file():
                matches.append(str(p.relative_to(ROOT_DIR)))
                if len(matches) >= 100:
                    break
    except Exception as e:
        return f"ERROR: {e}"
    return f"Found {len(matches)} file(s):\n" + "\n".join(matches) if matches else f"No files matching '{pattern}' in {path}"


def tool_search_code(pattern: str, path: str = ".", context_lines: int = 2) -> str:
    try:
        base = _guard_path(path)
    except PermissionError as e:
        return f"ERROR: {e}"
    context_lines = min(max(int(context_lines), 0), 5)
    # Try git grep first
    try:
        cmd = ["git", "grep", "-n", f"-C{context_lines}", "--max-count=20", "-E", pattern]
        if path != ".":
            cmd.append(str(base.relative_to(ROOT_DIR)))
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, cwd=str(ROOT_DIR),
                               encoding="utf-8", errors="replace")
        if result.returncode == 0 and result.stdout.strip():
            return result.stdout[:15000]
    except Exception:
        pass
    # Python fallback
    matches = []
    try:
        compiled = re.compile(pattern)
    except re.error as e:
        return f"ERROR: Invalid regex: {e}"
    _SKIP = (".pyc", ".pyo", ".so", ".dll", ".exe", ".bin", ".png", ".jpg", ".gif")
    try:
        for fp in base.rglob("*"):
            if not fp.is_file() or fp.stat().st_size > 500_000 or fp.suffix in _SKIP:
                continue
            try:
                lines = fp.read_text(encoding="utf-8", errors="replace").splitlines()
            except Exception:
                continue
            for i, line in enumerate(lines):
                if compiled.search(line):
                    rel = str(fp.relative_to(ROOT_DIR))
                    s, e = max(0, i - context_lines), min(len(lines), i + context_lines + 1)
                    snippet = "\n".join(f"  {j+1}: {lines[j]}" for j in range(s, e))
                    matches.append(f"{rel}:{i+1}\n{snippet}")
                    if len(matches) >= 20:
                        return f"Found {len(matches)}+ matches:\n\n" + "\n\n".join(matches)
    except Exception as e:
        return f"ERROR scanning files: {e}"
    return f"Found {len(matches)} match(es):\n\n" + "\n\n".join(matches) if matches else f"No matches for '{pattern}'"


try:
    from pulse.browser.core import browser_goto, browser_get_content, browser_fill, browser_click
except ImportError:
    browser_goto = browser_get_content = browser_fill = browser_click = None


# ---------------------------------------------------------------------------
# Re-export memory tools so all existing callers keep working with a single
# import path: `from pulse.workers.worker_tools_ext import ...`
# ---------------------------------------------------------------------------
from pulse.workers.worker_tools_memory import (  # noqa: E402, F401
    capture_to_bridge_log,
    save_session_json,
    tool_extract_knowledge_batch,
    tool_clean_intents_batch,
    tool_check_antipattern,
)
