"""
React Project Agent Tool

This tool can autonomously create, run, debug, and enhance React/Next.js projects.
"""

import os
import subprocess
import sys
import time
from typing import Dict, Any, List, Optional
from tools.tool_registry import BaseTool

class ReactProjectTool(BaseTool):
    """Agentic tool for full React/Next.js project lifecycle management"""
    
    def __init__(self):
        super().__init__(
            name="react_project_tool",
            description="Create, run, debug, and enhance React/Next.js projects with or without TypeScript. Can scaffold, install, run, debug, add pages/components, fix routes, and more."
        )
        self.project_dir = None
        self.dev_process = None

    def execute(self, query: str, context: Dict[str, Any]) -> Any:
        """Main entry point for the tool"""
        # Parse intent
        intent = self._parse_intent(query)
        if intent == "scaffold":
            return self._scaffold_project(query)
        elif intent == "install":
            return self._install_dependencies(query)
        elif intent == "run":
            return self._run_dev_server(query)
        elif intent == "add_page":
            return self._add_page(query)
        elif intent == "add_component":
            return self._add_component(query)
        elif intent == "fix":
            return self._fix_project(query)
        elif intent == "enhance":
            return self._enhance_project(query)
        elif intent == "status":
            return self._project_status(query)
        else:
            # Default: try to enhance or add code
            return self._enhance_project(query)

    def _parse_intent(self, query: str) -> str:
        q = query.lower()
        if any(word in q for word in ["create", "scaffold", "new project"]):
            return "scaffold"
        if any(word in q for word in ["install", "dependency", "dependencies"]):
            return "install"
        if any(word in q for word in ["run", "start dev", "start server"]):
            return "run"
        if "add page" in q or "new page" in q:
            return "add_page"
        if "add component" in q or "new component" in q:
            return "add_component"
        if any(word in q for word in ["fix", "debug", "error", "broken"]):
            return "fix"
        if any(word in q for word in ["enhance", "improve", "refactor", "upgrade"]):
            return "enhance"
        if any(word in q for word in ["status", "info", "show project"]):
            return "status"
        return "enhance"

    def _ensure_node(self) -> bool:
        """Ensure Node.js and npm are installed"""
        try:
            subprocess.check_call(["node", "--version"])
            subprocess.check_call(["npm", "--version"])
            return True
        except Exception:
            # Try to install Node.js (platform dependent)
            return False

    def _scaffold_project(self, query: str) -> Dict[str, Any]:
        """Scaffold a new React or Next.js project"""
        import re
        if not self._ensure_node():
            return {"success": False, "error": "Node.js and npm are required. Please install them manually."}
        # Determine project type and name
        use_next = "next" in query.lower()
        use_ts = "typescript" in query.lower() or "ts" in query.lower()
        name_match = re.search(r'project(?: called| named)? ([\w-]+)', query.lower())
        project_name = name_match.group(1) if name_match else "my-react-app"
        self.project_dir = os.path.abspath(project_name)
        if os.path.exists(self.project_dir):
            return {"success": False, "error": f"Directory {self.project_dir} already exists."}
        try:
            if use_next:
                cmd = ["npx", "create-next-app@latest", project_name]
                if use_ts:
                    cmd += ["--typescript"]
            else:
                cmd = ["npx", "create-react-app", project_name]
                if use_ts:
                    cmd += ["--template", "typescript"]
            subprocess.check_call(cmd)
            return {"success": True, "message": f"Project {project_name} created.", "project_dir": self.project_dir}
        except Exception as e:
            return {"success": False, "error": str(e)}

    def _install_dependencies(self, query: str) -> Dict[str, Any]:
        """Install npm dependencies in the project directory"""
        import re
        if not self.project_dir:
            return {"success": False, "error": "No project directory set. Scaffold a project first."}
        dep_match = re.findall(r'install ([\w@/\-]+)', query.lower())
        deps = dep_match if dep_match else []
        try:
            if deps:
                cmd = ["npm", "install"] + deps
            else:
                cmd = ["npm", "install"]
            subprocess.check_call(cmd, cwd=self.project_dir)
            return {"success": True, "message": f"Dependencies installed: {' '.join(deps) if deps else 'all'}"}
        except Exception as e:
            return {"success": False, "error": str(e)}

    def _run_dev_server(self, query: str) -> Dict[str, Any]:
        """Run the dev server in the background"""
        if not self.project_dir:
            return {"success": False, "error": "No project directory set. Scaffold a project first."}
        try:
            if self.dev_process and self.dev_process.poll() is None:
                return {"success": True, "message": "Dev server already running."}
            cmd = ["npm", "run", "dev"] if os.path.exists(os.path.join(self.project_dir, "next.config.js")) else ["npm", "start"]
            self.dev_process = subprocess.Popen(cmd, cwd=self.project_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            time.sleep(3)
            if self.dev_process.poll() is None:
                return {"success": True, "message": "Dev server started.", "pid": self.dev_process.pid}
            else:
                out, err = self.dev_process.communicate()
                return {"success": False, "error": err.decode()}
        except Exception as e:
            return {"success": False, "error": str(e)}

    def _add_page(self, query: str) -> Dict[str, Any]:
        """Add a new page to the project (Next.js or React Router)"""
        import re
        if not self.project_dir:
            return {"success": False, "error": "No project directory set. Scaffold a project first."}
        page_match = re.search(r'add page ([\w/-]+)', query.lower())
        page_name = page_match.group(1) if page_match else "newpage"
        use_next = os.path.exists(os.path.join(self.project_dir, "next.config.js"))
        try:
            if use_next:
                pages_dir = os.path.join(self.project_dir, "pages")
                os.makedirs(pages_dir, exist_ok=True)
                page_path = os.path.join(pages_dir, f"{page_name}.tsx")
                with open(page_path, "w") as f:
                    f.write(self._default_next_page(page_name))
            else:
                src_dir = os.path.join(self.project_dir, "src")
                pages_dir = os.path.join(src_dir, "pages")
                os.makedirs(pages_dir, exist_ok=True)
                page_path = os.path.join(pages_dir, f"{page_name}.tsx")
                with open(page_path, "w") as f:
                    f.write(self._default_react_page(page_name))
            return {"success": True, "message": f"Page {page_name} added.", "page_path": page_path}
        except Exception as e:
            return {"success": False, "error": str(e)}

    def _add_component(self, query: str) -> Dict[str, Any]:
        """Add a new component to the project"""
        import re
        if not self.project_dir:
            return {"success": False, "error": "No project directory set. Scaffold a project first."}
        comp_match = re.search(r'add component ([\w/-]+)', query.lower())
        comp_name = comp_match.group(1) if comp_match else "NewComponent"
        src_dir = os.path.join(self.project_dir, "src")
        comp_dir = os.path.join(src_dir, "components")
        os.makedirs(comp_dir, exist_ok=True)
        comp_path = os.path.join(comp_dir, f"{comp_name}.tsx")
        try:
            with open(comp_path, "w") as f:
                f.write(self._default_component(comp_name))
            return {"success": True, "message": f"Component {comp_name} added.", "component_path": comp_path}
        except Exception as e:
            return {"success": False, "error": str(e)}

    def _fix_project(self, query: str) -> Dict[str, Any]:
        """Attempt to fix common project errors (missing deps, syntax, etc.)"""
        # For now, just run npm install and try to parse errors
        try:
            install_result = self._install_dependencies("install")
            return {"success": True, "message": "Attempted to fix project by reinstalling dependencies.", "details": install_result}
        except Exception as e:
            return {"success": False, "error": str(e)}

    def _enhance_project(self, query: str) -> Dict[str, Any]:
        """Enhance or refactor the project based on user instructions (uses LLM if needed)"""
        # Use LLM to generate/refactor code based on the query
        if not self.project_dir:
            return {"success": False, "error": "No project directory set. Scaffold a project first."}
        try:
            llm_result = self._llm_generate_code(query)
            if not llm_result or 'code' not in llm_result:
                return {"success": False, "error": "LLM did not return code."}
            # Write the generated code to the appropriate file (simple heuristic: look for filename in query)
            import re
            file_match = re.search(r'(src/[\w/]+\.tsx?)', query)
            file_path = file_match.group(1) if file_match else None
            if not file_path:
                # Default to a new file
                file_path = os.path.join(self.project_dir, "src", "EnhancedComponent.tsx")
            else:
                file_path = os.path.join(self.project_dir, file_path) if not os.path.isabs(file_path) else file_path
            os.makedirs(os.path.dirname(file_path), exist_ok=True)
            with open(file_path, "w") as f:
                f.write(llm_result['code'])
            return {"success": True, "message": "Enhancement applied via LLM.", "file": file_path, "llm_response": llm_result}
        except Exception as e:
            return {"success": False, "error": str(e)}

    def _llm_generate_code(self, query: str) -> Dict[str, Any]:
        """Call OpenAI API to generate or refactor code based on the query"""
        try:
            import openai
            from openai import OpenAI
            client = OpenAI()
            prompt = f"""
You are an expert React/Next.js developer. Given the following instruction, generate or refactor the appropriate TypeScript React code. Only output the code, no explanations.

Instruction: {query}
"""
            response = client.chat.completions.create(
                model="gpt-4o",
                messages=[{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": prompt}],
                temperature=0.2
            )
            code = response.choices[0].message.content.strip()
            # Remove markdown code block if present
            if code.startswith("```"):
                code = code.split('```')[1]
                if code.startswith("typescript") or code.startswith("tsx") or code.startswith("jsx"):
                    code = '\n'.join(code.split('\n')[1:])
            return {"code": code}
        except Exception as e:
            return {"error": str(e)}

    def _project_status(self, query: str) -> Dict[str, Any]:
        """Return project status"""
        if not self.project_dir:
            return {"success": False, "error": "No project directory set."}
        return {"success": True, "project_dir": self.project_dir, "dev_server_running": self.dev_process is not None and self.dev_process.poll() is None}

    def _default_next_page(self, page_name: str) -> str:
        return f"""import React from 'react';\n\nexport default function {page_name.title()}() {{\n  return <div>{page_name.title()} page</div>;\n}}\n"""

    def _default_react_page(self, page_name: str) -> str:
        return f"""import React from 'react';\n\nexport default function {page_name.title()}() {{\n  return <div>{page_name.title()} page</div>;\n}}\n"""

    def _default_component(self, comp_name: str) -> str:
        return f"""import React from 'react';\n\ninterface {comp_name}Props {{}}\n\nconst {comp_name}: React.FC<{comp_name}Props> = () => {{\n  return <div>{comp_name} component</div>;\n}};\n\nexport default {comp_name};\n"""

    def get_capabilities(self) -> List[str]:
        return [
            "scaffold_project", "install_dependencies", "run_dev_server", "add_page", "add_component", "fix_project", "enhance_project", "project_status"
        ]

    def validate_input(self, query: str, context: Dict[str, Any]) -> bool:
        return any(word in query.lower() for word in ["react", "next", "component", "page", "typescript", "scaffold", "project", "fix", "enhance", "run", "install"]) 