# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Desktop Node Tools

Tools that run on desktop/laptop nodes and can be called remotely.
These expose local machine capabilities to the gateway.
"""

import os
import platform
import subprocess
from datetime import datetime
from pathlib import Path


def get_clipboard(data: dict) -> str:
    """Get clipboard contents."""
    system = platform.system()

    try:
        if system == "Darwin":  # macOS
            result = subprocess.run(["pbpaste"], capture_output=True, text=True)
            return result.stdout

        elif system == "Linux":
            # Try xclip first, then xsel
            try:
                result = subprocess.run(
                    ["xclip", "-selection", "clipboard", "-o"], capture_output=True, text=True
                )
                return result.stdout
            except (subprocess.SubprocessError, FileNotFoundError):
                result = subprocess.run(
                    ["xsel", "--clipboard", "--output"], capture_output=True, text=True
                )
                return result.stdout

        elif system == "Windows":
            import win32clipboard

            win32clipboard.OpenClipboard()
            try:
                data = win32clipboard.GetClipboardData()
                return data
            finally:
                win32clipboard.CloseClipboard()

        return "Clipboard access not available"

    except Exception as e:
        return f"Error: {e}"


def set_clipboard(data: dict) -> str:
    """Set clipboard contents."""
    text = data.get("text", "")
    system = platform.system()

    try:
        if system == "Darwin":
            subprocess.run(["pbcopy"], input=text.encode(), check=True)
            return "✓ Copied to clipboard"

        elif system == "Linux":
            try:
                subprocess.run(
                    ["xclip", "-selection", "clipboard"], input=text.encode(), check=True
                )
            except (subprocess.SubprocessError, FileNotFoundError):
                subprocess.run(["xsel", "--clipboard", "--input"], input=text.encode(), check=True)
            return "✓ Copied to clipboard"

        elif system == "Windows":
            import win32clipboard

            win32clipboard.OpenClipboard()
            try:
                win32clipboard.EmptyClipboard()
                win32clipboard.SetClipboardText(text)
            finally:
                win32clipboard.CloseClipboard()
            return "✓ Copied to clipboard"

        return "Clipboard access not available"

    except Exception as e:
        return f"Error: {e}"


def show_notification(data: dict) -> str:
    """Show a desktop notification."""
    title = data.get("title", "Familiar")
    message = data.get("message", "")
    system = platform.system()

    try:
        if system == "Darwin":
            script = f'display notification "{message}" with title "{title}"'
            subprocess.run(["osascript", "-e", script], check=True)
            return "✓ Notification sent"

        elif system == "Linux":
            subprocess.run(["notify-send", title, message], check=True)
            return "✓ Notification sent"

        elif system == "Windows":
            # Using PowerShell for toast notification
            ps_script = f'''
            [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
            $template = [Windows.UI.Notifications.ToastTemplateType]::ToastText02
            $xml = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent($template)
            $xml.GetElementsByTagName("text")[0].AppendChild($xml.CreateTextNode("{title}"))
            $xml.GetElementsByTagName("text")[1].AppendChild($xml.CreateTextNode("{message}"))
            $toast = [Windows.UI.Notifications.ToastNotification]::new($xml)
            [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Familiar").Show($toast)
            '''
            subprocess.run(["powershell", "-Command", ps_script], check=True)
            return "✓ Notification sent"

        return "Notifications not available"

    except Exception as e:
        return f"Error: {e}"


def open_url(data: dict) -> str:
    """Open URL in default browser."""
    url = data.get("url", "")
    if not url:
        return "No URL provided"

    import webbrowser

    webbrowser.open(url)
    return f"✓ Opened {url}"


def open_file(data: dict) -> str:
    """Open file with default application."""
    path = data.get("path", "")
    if not path:
        return "No path provided"

    path = Path(path).expanduser()
    if not path.exists():
        return f"File not found: {path}"

    system = platform.system()

    try:
        if system == "Darwin":
            subprocess.run(["open", str(path)], check=True)
        elif system == "Linux":
            subprocess.run(["xdg-open", str(path)], check=True)
        elif system == "Windows":
            os.startfile(str(path))

        return f"✓ Opened {path}"

    except Exception as e:
        return f"Error: {e}"


def get_selected_text(data: dict) -> str:
    """Get currently selected text (macOS only for now)."""
    system = platform.system()

    if system == "Darwin":
        try:
            # This requires accessibility permissions
            script = """
            tell application "System Events"
                keystroke "c" using command down
            end tell
            delay 0.1
            return the clipboard
            """
            result = subprocess.run(["osascript", "-e", script], capture_output=True, text=True)
            return result.stdout.strip()
        except Exception as e:
            return f"Error: {e}"

    return "Selection access not available on this platform"


def type_text(data: dict) -> str:
    """Type text as if from keyboard."""
    text = data.get("text", "")
    if not text:
        return "No text provided"

    system = platform.system()

    try:
        if system == "Darwin":
            # Escape special characters for AppleScript
            escaped = text.replace("\\", "\\\\").replace('"', '\\"')
            script = f'tell application "System Events" to keystroke "{escaped}"'
            subprocess.run(["osascript", "-e", script], check=True)
            return "✓ Text typed"

        elif system == "Linux":
            subprocess.run(["xdotool", "type", "--", text], check=True)
            return "✓ Text typed"

        return "Typing not available on this platform"

    except Exception as e:
        return f"Error: {e}"


def take_screenshot(data: dict) -> str:
    """Take a screenshot."""
    filename = data.get("filename", f"screenshot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png")
    path = Path.home() / "Desktop" / filename

    system = platform.system()

    try:
        if system == "Darwin":
            subprocess.run(["screencapture", "-x", str(path)], check=True)

        elif system == "Linux":
            # Try various screenshot tools
            try:
                subprocess.run(["gnome-screenshot", "-f", str(path)], check=True)
            except (subprocess.SubprocessError, FileNotFoundError):
                try:
                    subprocess.run(["scrot", str(path)], check=True)
                except (subprocess.SubprocessError, FileNotFoundError):
                    subprocess.run(["import", "-window", "root", str(path)], check=True)

        elif system == "Windows":
            # Use PowerShell
            ps_script = f'''
            Add-Type -AssemblyName System.Windows.Forms
            [System.Windows.Forms.Screen]::PrimaryScreen | ForEach-Object {{
                $bitmap = New-Object System.Drawing.Bitmap($_.Bounds.Width, $_.Bounds.Height)
                $graphics = [System.Drawing.Graphics]::FromImage($bitmap)
                $graphics.CopyFromScreen($_.Bounds.Location, [System.Drawing.Point]::Empty, $_.Bounds.Size)
                $bitmap.Save("{path}")
            }}
            '''
            subprocess.run(["powershell", "-Command", ps_script], check=True)

        return f"✓ Screenshot saved: {path}"

    except Exception as e:
        return f"Error: {e}"


def get_active_window(data: dict) -> str:
    """Get info about the active window."""
    system = platform.system()

    try:
        if system == "Darwin":
            script = """
            tell application "System Events"
                set frontApp to name of first application process whose frontmost is true
                set windowTitle to name of front window of (first application process whose frontmost is true)
                return frontApp & " - " & windowTitle
            end tell
            """
            result = subprocess.run(["osascript", "-e", script], capture_output=True, text=True)
            return result.stdout.strip()

        elif system == "Linux":
            result = subprocess.run(
                ["xdotool", "getactivewindow", "getwindowname"], capture_output=True, text=True
            )
            return result.stdout.strip()

        return "Window info not available"

    except Exception as e:
        return f"Error: {e}"


def list_running_apps(data: dict) -> str:
    """List running applications."""
    system = platform.system()

    try:
        if system == "Darwin":
            script = """
            tell application "System Events"
                set appNames to name of every application process whose background only is false
                return appNames
            end tell
            """
            result = subprocess.run(["osascript", "-e", script], capture_output=True, text=True)
            apps = result.stdout.strip().split(", ")
            return "Running apps:\n" + "\n".join(f"  • {app}" for app in apps)

        elif system == "Linux":
            result = subprocess.run(["wmctrl", "-l"], capture_output=True, text=True)
            return f"Windows:\n{result.stdout}"

        return "App list not available"

    except Exception as e:
        return f"Error: {e}"


# Tool definitions for registration with node
DESKTOP_TOOLS = [
    {
        "name": "clipboard_get",
        "description": "Get the current clipboard contents",
        "input_schema": {"type": "object", "properties": {}},
        "handler": get_clipboard,
    },
    {
        "name": "clipboard_set",
        "description": "Copy text to the clipboard",
        "input_schema": {
            "type": "object",
            "properties": {"text": {"type": "string", "description": "Text to copy"}},
            "required": ["text"],
        },
        "handler": set_clipboard,
    },
    {
        "name": "notify",
        "description": "Show a desktop notification",
        "input_schema": {
            "type": "object",
            "properties": {
                "title": {"type": "string", "default": "Familiar"},
                "message": {"type": "string", "description": "Notification message"},
            },
            "required": ["message"],
        },
        "handler": show_notification,
    },
    {
        "name": "open_url",
        "description": "Open a URL in the default browser",
        "input_schema": {
            "type": "object",
            "properties": {"url": {"type": "string"}},
            "required": ["url"],
        },
        "handler": open_url,
    },
    {
        "name": "open_file",
        "description": "Open a file with its default application",
        "input_schema": {
            "type": "object",
            "properties": {"path": {"type": "string", "description": "File path"}},
            "required": ["path"],
        },
        "handler": open_file,
    },
    {
        "name": "type_text",
        "description": "Type text as keyboard input",
        "input_schema": {
            "type": "object",
            "properties": {"text": {"type": "string"}},
            "required": ["text"],
        },
        "handler": type_text,
    },
    {
        "name": "screenshot",
        "description": "Take a screenshot of the screen",
        "input_schema": {
            "type": "object",
            "properties": {"filename": {"type": "string", "description": "Optional filename"}},
        },
        "handler": take_screenshot,
    },
    {
        "name": "active_window",
        "description": "Get information about the currently active window",
        "input_schema": {"type": "object", "properties": {}},
        "handler": get_active_window,
    },
    {
        "name": "running_apps",
        "description": "List currently running applications",
        "input_schema": {"type": "object", "properties": {}},
        "handler": list_running_apps,
    },
]


def register_desktop_tools(node):
    """Register all desktop tools with a mesh node."""
    for tool in DESKTOP_TOOLS:
        node.register_tool(
            name=tool["name"],
            description=tool["description"],
            schema=tool["input_schema"],
            handler=tool["handler"],
        )
