#!/usr/bin/env python3
"""
Timmy Tool Commands
Issue #84 — Bridge Tools into Evennia

Converts Timmy's tool library into Evennia Command objects
so they can be invoked within the world.
"""

from evennia import Command
from evennia.utils import evtable
from typing import Optional, List
import json
import os


class CmdRead(Command):
    """
    Read a file from the system.
    
    Usage:
      read <path>
    
    Example:
      read ~/.timmy/config.yaml
      read /opt/timmy/logs/latest.log
    """
    
    key = "read"
    aliases = ["cat", "show"]
    help_category = "Tools"
    
    def func(self):
        if not self.args:
            self.caller.msg("Usage: read <path>")
            return
        
        path = self.args.strip()
        path = os.path.expanduser(path)
        
        try:
            with open(path, 'r') as f:
                content = f.read()
            
            # Store for later use
            self.caller.db.last_read_file = path
            self.caller.db.last_read_content = content
            
            # Limit display if too long
            lines = content.split('\n')
            if len(lines) > 50:
                display = '\n'.join(lines[:50])
                self.caller.msg(f"|w{path}|n (showing first 50 lines of {len(lines)}):")
                self.caller.msg(display)
                self.caller.msg(f"\n|y... {len(lines) - 50} more lines|n")
            else:
                self.caller.msg(f"|w{path}|n:")
                self.caller.msg(content)
            
            # Record in metrics
            if hasattr(self.caller, 'update_metrics'):
                self.caller.update_metrics(files_read=1)
            
        except FileNotFoundError:
            self.caller.msg(f"|rFile not found:|n {path}")
        except PermissionError:
            self.caller.msg(f"|rPermission denied:|n {path}")
        except Exception as e:
            self.caller.msg(f"|rError reading file:|n {e}")


class CmdWrite(Command):
    """
    Write content to a file.
    
    Usage:
      write <path> = <content>
    
    Example:
      write ~/.timmy/notes.txt = This is a note
    """
    
    key = "write"
    aliases = ["save"]
    help_category = "Tools"
    
    def func(self):
        if not self.args or "=" not in self.args:
            self.caller.msg("Usage: write <path> = <content>")
            return
        
        path, content = self.args.split("=", 1)
        path = path.strip()
        content = content.strip()
        path = os.path.expanduser(path)
        
        try:
            # Create directory if needed
            os.makedirs(os.path.dirname(path), exist_ok=True)
            
            with open(path, 'w') as f:
                f.write(content)
            
            self.caller.msg(f"|gWritten:|n {path}")
            
            # Update metrics
            if hasattr(self.caller, 'update_metrics'):
                self.caller.update_metrics(files_modified=1, lines_written=content.count('\n'))
            
        except PermissionError:
            self.caller.msg(f"|rPermission denied:|n {path}")
        except Exception as e:
            self.caller.msg(f"|rError writing file:|n {e}")


class CmdSearch(Command):
    """
    Search file contents for a pattern.
    
    Usage:
      search <pattern> [in <path>]
    
    Example:
      search "def main" in ~/code/
      search "TODO"
    """
    
    key = "search"
    aliases = ["grep", "find"]
    help_category = "Tools"
    
    def func(self):
        if not self.args:
            self.caller.msg("Usage: search <pattern> [in <path>]")
            return
        
        args = self.args.strip()
        
        # Parse path if specified
        if " in " in args:
            pattern, path = args.split(" in ", 1)
            pattern = pattern.strip()
            path = path.strip()
        else:
            pattern = args
            path = "."
        
        path = os.path.expanduser(path)
        
        try:
            import subprocess
            result = subprocess.run(
                ["grep", "-r", "-n", pattern, path],
                capture_output=True,
                text=True,
                timeout=10
            )
            
            if result.returncode == 0:
                lines = result.stdout.strip().split('\n')
                self.caller.msg(f"|gFound {len(lines)} matches for '|n{pattern}|g':|n")
                for line in lines[:20]:  # Limit output
                    self.caller.msg(f"  {line}")
                if len(lines) > 20:
                    self.caller.msg(f"\n|y... and {len(lines) - 20} more|n")
            else:
                self.caller.msg(f"|yNo matches found for '|n{pattern}|y'|n")
                
        except subprocess.TimeoutExpired:
            self.caller.msg("|rSearch timed out|n")
        except Exception as e:
            self.caller.msg(f"|rError searching:|n {e}")


class CmdGitStatus(Command):
    """
    Check git status of a repository.
    
    Usage:
      git status [path]
    
    Example:
      git status
      git status ~/projects/timmy
    """
    
    key = "git_status"
    aliases = ["git status"]
    help_category = "Git"
    
    def func(self):
        path = self.args.strip() if self.args else "."
        path = os.path.expanduser(path)
        
        try:
            import subprocess
            result = subprocess.run(
                ["git", "-C", path, "status", "-sb"],
                capture_output=True,
                text=True
            )
            
            if result.returncode == 0:
                self.caller.msg(f"|wGit status ({path}):|n")
                self.caller.msg(result.stdout)
            else:
                self.caller.msg(f"|rNot a git repository:|n {path}")
                
        except Exception as e:
            self.caller.msg(f"|rError:|n {e}")


class CmdGitLog(Command):
    """
    Show git commit history.
    
    Usage:
      git log [n] [path]
    
    Example:
      git log
      git log 10
      git log 5 ~/projects/timmy
    """
    
    key = "git_log"
    aliases = ["git log"]
    help_category = "Git"
    
    def func(self):
        args = self.args.strip().split() if self.args else []
        
        # Parse args
        path = "."
        n = 10
        
        for arg in args:
            if arg.isdigit():
                n = int(arg)
            else:
                path = arg
        
        path = os.path.expanduser(path)
        
        try:
            import subprocess
            result = subprocess.run(
                ["git", "-C", path, "log", f"--oneline", f"-{n}"],
                capture_output=True,
                text=True
            )
            
            if result.returncode == 0:
                self.caller.msg(f"|wRecent commits ({path}):|n")
                self.caller.msg(result.stdout)
            else:
                self.caller.msg(f"|rNot a git repository:|n {path}")
                
        except Exception as e:
            self.caller.msg(f"|rError:|n {e}")


class CmdGitPull(Command):
    """
    Pull latest changes from git remote.
    
    Usage:
      git pull [path]
    """
    
    key = "git_pull"
    aliases = ["git pull"]
    help_category = "Git"
    
    def func(self):
        path = self.args.strip() if self.args else "."
        path = os.path.expanduser(path)
        
        try:
            import subprocess
            result = subprocess.run(
                ["git", "-C", path, "pull"],
                capture_output=True,
                text=True
            )
            
            if result.returncode == 0:
                self.caller.msg(f"|gPulled ({path}):|n")
                self.caller.msg(result.stdout)
            else:
                self.caller.msg(f"|rPull failed:|n {result.stderr}")
                
        except Exception as e:
            self.caller.msg(f"|rError:|n {e}")


class CmdSysInfo(Command):
    """
    Display system information.
    
    Usage:
      sysinfo
    """
    
    key = "sysinfo"
    aliases = ["system_info", "status"]
    help_category = "System"
    
    def func(self):
        import platform
        import psutil
        
        # Gather info
        info = {
            "Platform": platform.platform(),
            "CPU": f"{psutil.cpu_count()} cores, {psutil.cpu_percent()}% used",
            "Memory": f"{psutil.virtual_memory().percent}% used "
                      f"({psutil.virtual_memory().used // (1024**3)}GB / "
                      f"{psutil.virtual_memory().total // (1024**3)}GB)",
            "Disk": f"{psutil.disk_usage('/').percent}% used "
                    f"({psutil.disk_usage('/').free // (1024**3)}GB free)",
            "Uptime": f"{psutil.boot_time()}"  # Simplified
        }
        
        self.caller.msg("|wSystem Information:|n")
        for key, value in info.items():
            self.caller.msg(f"  |c{key}|n: {value}")


class CmdHealth(Command):
    """
    Check health of Timmy services.
    
    Usage:
      health
    """
    
    key = "health"
    aliases = ["check"]
    help_category = "System"
    
    def func(self):
        import subprocess
        
        services = [
            "timmy-overnight-loop",
            "timmy-health",
            "llama-server",
            "gitea"
        ]
        
        self.caller.msg("|wService Health:|n")
        
        for service in services:
            try:
                result = subprocess.run(
                    ["systemctl", "is-active", service],
                    capture_output=True,
                    text=True
                )
                status = result.stdout.strip()
                icon = "|g●|n" if status == "active" else "|r●|n"
                self.caller.msg(f"  {icon} {service}: {status}")
            except:
                self.caller.msg(f"  |y?|n {service}: unknown")


class CmdThink(Command):
    """
    Send a prompt to the local LLM and return the response.
    
    Usage:
      think <prompt>
    
    Example:
      think What should I focus on today?
      think Summarize the last git commit
    """
    
    key = "think"
    aliases = ["reason", "ponder"]
    help_category = "Inference"
    
    def func(self):
        if not self.args:
            self.caller.msg("Usage: think <prompt>")
            return
        
        prompt = self.args.strip()
        
        self.caller.msg(f"|wThinking about:|n {prompt[:50]}...")
        
        try:
            import requests
            
            response = requests.post(
                "http://localhost:8080/v1/chat/completions",
                json={
                    "model": "hermes4",
                    "messages": [
                        {"role": "user", "content": prompt}
                    ],
                    "max_tokens": 500
                },
                timeout=60
            )
            
            if response.status_code == 200:
                result = response.json()
                content = result["choices"][0]["message"]["content"]
                self.caller.msg(f"\n|cResponse:|n\n{content}")
            else:
                self.caller.msg(f"|rError:|n HTTP {response.status_code}")
                
        except requests.exceptions.ConnectionError:
            self.caller.msg("|rError:|n llama-server not running on localhost:8080")
        except Exception as e:
            self.caller.msg(f"|rError:|n {e}")


class CmdGiteaIssues(Command):
    """
    List open issues from Gitea.
    
    Usage:
      gitea issues
      gitea issues --limit 5
    """
    
    key = "gitea_issues"
    aliases = ["issues"]
    help_category = "Gitea"
    
    def func(self):
        args = self.args.strip().split() if self.args else []
        limit = 10
        
        for i, arg in enumerate(args):
            if arg == "--limit" and i + 1 < len(args):
                limit = int(args[i + 1])
        
        try:
            import requests
            
            # Get issues from Gitea API
            response = requests.get(
                "http://143.198.27.163:3000/api/v1/repos/Timmy_Foundation/timmy-home/issues",
                params={"state": "open", "limit": limit},
                timeout=10
            )
            
            if response.status_code == 200:
                issues = response.json()
                self.caller.msg(f"|wOpen Issues ({len(issues)}):|n\n")
                
                for issue in issues:
                    num = issue["number"]
                    title = issue["title"][:60]
                    assignee = issue.get("assignee", {}).get("login", "unassigned")
                    self.caller.msg(f"  |y#{num}|n: {title} (|c{assignee}|n)")
            else:
                self.caller.msg(f"|rError:|n HTTP {response.status_code}")
                
        except Exception as e:
            self.caller.msg(f"|rError:|n {e}")


class CmdWorkshop(Command):
    """
    Enter the Workshop room.
    
    Usage:
      workshop
    """
    
    key = "workshop"
    help_category = "Navigation"
    
    def func(self):
        # Find workshop
        workshop = self.caller.search("Workshop", global_search=True)
        if workshop:
            self.caller.move_to(workshop)


class CmdLibrary(Command):
    """
    Enter the Library room.
    
    Usage:
      library
    """
    
    key = "library"
    help_category = "Navigation"
    
    def func(self):
        library = self.caller.search("Library", global_search=True)
        if library:
            self.caller.move_to(library)


class CmdObservatory(Command):
    """
    Enter the Observatory room.
    
    Usage:
      observatory
    """
    
    key = "observatory"
    help_category = "Navigation"
    
    def func(self):
        obs = self.caller.search("Observatory", global_search=True)
        if obs:
            self.caller.move_to(obs)


class CmdStatus(Command):
    """
    Show Timmy's current status.
    
    Usage:
      status
    """
    
    key = "status"
    help_category = "Info"
    
    def func(self):
        if hasattr(self.caller, 'get_status'):
            status = self.caller.get_status()
            
            self.caller.msg("|wTimmy Status:|n\n")
            
            if status.get('current_task'):
                self.caller.msg(f"|yCurrent Task:|n {status['current_task']['description']}")
            else:
                self.caller.msg("|gNo active task|n")
            
            self.caller.msg(f"Tasks Completed: {status['tasks_completed']}")
            self.caller.msg(f"Knowledge Items: {status['knowledge_items']}")
            self.caller.msg(f"Tools Available: {status['tools_available']}")
            self.caller.msg(f"Location: {status['location']}")
        else:
            self.caller.msg("Status not available.")
