from typing import List, Dict, Any, Optional
import os
import time
import asyncio
import logging
import urllib.request
import urllib.error
import json
from pathlib import Path
from .base import Capability
from ... import commitment_ledger

logger = logging.getLogger("nucleus.render_poller")

# Global dict to store active polls in memory
# {service_id: {"commit_sha": str, "start_time": float, "status": str, "task": asyncio.Task}}
ACTIVE_POLLS = {}

class RenderPolling(Capability):
    def __init__(self):
        self._brain_path = Path(os.environ.get("NUCLEAR_BRAIN_PATH", "/Users/lokeshgarg/.gemini/antigravity/brain/7c654df4-b83e-43f9-8620-f15868ec39d1"))
        self._api_key = os.environ.get("RENDER_API_KEY")
        
    @property
    def name(self) -> str:
        return "render_poller"

    @property
    def description(self) -> str:
        return "Automated deployment monitoring for Render services."

    def get_tools(self) -> List[Dict[str, Any]]:
        return [
            {
                "name": "brain_start_deploy_poll",
                "description": "Start monitoring a Render deploy in the background.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "service_id": {"type": "string", "description": "Render service ID (e.g., 'srv-abc123')"},
                        "commit_sha": {"type": "string", "description": "Optional Git commit SHA being deployed"}
                    },
                    "required": ["service_id"]
                }
            },
            {
                "name": "brain_check_deploy",
                "description": "Check status of an active deploy poll.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "service_id": {"type": "string", "description": "Render service ID to check"}
                    },
                    "required": ["service_id"]
                }
            },
            {
                "name": "brain_smoke_test",
                "description": "Run a smoke test on any URL.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "url": {"type": "string", "description": "Base URL of service (e.g., 'https://myapp.onrender.com')"},
                        "endpoint": {"type": "string", "description": "Health endpoint to hit (default: '/api/health')"}
                    },
                    "required": ["url"]
                }
            }
        ]

    def execute_tool(self, tool_name: str, args: Dict) -> Any:
        try:
            if tool_name == "brain_start_deploy_poll":
                return self._start_poll(args.get("service_id"), args.get("commit_sha"))
            elif tool_name == "brain_check_deploy":
                return self._check_poll(args.get("service_id"))
            elif tool_name == "brain_smoke_test":
                return self._smoke_test(args.get("url"), args.get("endpoint", "/api/health"))
            return f"Tool {tool_name} not found"
        except Exception as e:
            return f"Error executing {tool_name}: {str(e)}"

    def _start_poll(self, service_id: str, commit_sha: Optional[str] = None) -> Dict:
        """Start a background polling task."""
        if not service_id:
            return {"error": "service_id required"}

        # Cancel existing poll for this service
        if service_id in ACTIVE_POLLS:
            existing = ACTIVE_POLLS[service_id]
            if not existing["task"].done():
                existing["task"].cancel()
                logger.info(f"Cancelled existing poll for {service_id}")

        # Start new task
        task = asyncio.create_task(self._poll_loop(service_id, commit_sha))
        
        ACTIVE_POLLS[service_id] = {
            "commit_sha": commit_sha or "latest",
            "start_time": time.time(),
            "status": "polling",
            "task": task,
            "logs": []
        }
        
        # Log event
        try:
            from ... import _emit_event
            _emit_event("deploy_poll_started", "render_poller", {
                "service_id": service_id,
                "commit_sha": commit_sha
            })
        except ImportError:
            pass # Circular import or not available

        return {
            "success": True, 
            "message": f"Started polling {service_id}",
            "poll_id": f"poll_{service_id}_{int(time.time())}"
        }

    def _check_poll(self, service_id: str) -> Dict:
        """Check status of an active poll."""
        if service_id not in ACTIVE_POLLS:
            return {"status": "not_found", "message": "No active poll for this service."}
        
        poll = ACTIVE_POLLS[service_id]
        uptime = int(time.time() - poll["start_time"])
        
        # Check if task is done (failed or finished)
        if poll["task"].done():
             try:
                 result = poll["task"].result()
                 return {
                     "status": "complete",
                     "result": result,
                     "uptime_seconds": uptime
                 }
             except asyncio.CancelledError:
                 return {"status": "cancelled", "uptime_seconds": uptime}
             except Exception as e:
                 return {"status": "error", "error": str(e), "uptime_seconds": uptime}
        
        return {
            "status": "polling",
            "uptime_seconds": uptime,
            "commit_sha": poll["commit_sha"]
        }

    async def _poll_loop(self, service_id: str, commit_sha: Optional[str]):
        """Async loop to check Render API."""
        timeout_mins = 20
        start_time = time.time()
        
        if not self._api_key:
            # Simulation mode if no key
            await asyncio.sleep(2)
            if service_id == "srv-test":
                await asyncio.sleep(1)
                return {"success": True, "url": "https://test-app.onrender.com", "simulated": True}
                
            raise ValueError("RENDER_API_KEY not set")

        headers = {"Authorization": f"Bearer {self._api_key}"}
        url = f"https://api.render.com/v1/services/{service_id}/deploys?limit=1"
        req = urllib.request.Request(url, headers=headers)
        
        while time.time() - start_time < (timeout_mins * 60):
            try:
                loop = asyncio.get_running_loop()
                # Use run_in_executor for blocking urlopen
                msg = await loop.run_in_executor(None, lambda: self._fetch_url(req))
                
                if "error" in msg:
                    logger.error(f"Render API error: {msg['error']}")
                else:
                    data = msg.get("json", [])
                    if data:
                        latest = data[0].get("deploy")
                        if latest:
                            status = latest.get("status")
                            # Match commit if provided
                            latest_commit = latest.get("commit", {}).get("id")
                            if commit_sha and latest_commit and not latest_commit.startswith(commit_sha):
                                # Not our commit yet
                                pass
                            elif status == "live":
                                deploy_url = f"https://{service_id}.onrender.com" # Simplification
                                
                                # Run smoke test!
                                smoke = self._smoke_test(deploy_url)
                                
                                # Log complete
                                try:
                                    from ... import _emit_event
                                    _emit_event("deploy_complete", "render_poller", {
                                        "service_id": service_id,
                                        "status": "live",
                                        "url": deploy_url,
                                        "smoke_test": smoke
                                    })
                                except: pass
                                
                                return {
                                    "success": True,
                                    "url": deploy_url,
                                    "smoke_test": smoke
                                }
                            elif status in ["build_failed", "update_failed", "canceled"]:
                                return {"success": False, "status": status}
            
            except Exception as e:
                logger.error(f"Poll error: {e}")

            await asyncio.sleep(30)
            
        return {"success": False, "error": "Timeout"}
        
    def _fetch_url(self, req) -> Dict:
        """Helper to fetch URL synchronously."""
        try:
            with urllib.request.urlopen(req, timeout=10) as response:
                if response.status == 200:
                    return {"json": json.load(response)}
                return {"error": f"HTTP {response.status}"}
        except Exception as e:
            return {"error": str(e)}

    def _smoke_test(self, url: str, endpoint: str = "/api/health") -> Dict:
        """Run a smoke test."""
        try:
            target = f"{url.rstrip('/')}{endpoint}"
            start = time.time()
            
            req = urllib.request.Request(target)
            try:
                with urllib.request.urlopen(req, timeout=10) as response:
                    latency = (time.time() - start) * 1000
                    passed = response.status == 200
                    # Check JSON content if valid
                    try:
                        data = json.load(response)
                        if isinstance(data, dict) and data.get("status") == "healthy":
                            passed = True
                    except: pass
                    
                    return {
                        "passed": passed,
                        "status_code": response.status,
                        "latency_ms": latency
                    }
            except urllib.error.HTTPError as e:
                 return {
                    "passed": False,
                    "status_code": e.code,
                    "error": str(e)
                }
            except Exception as e:
                 return {
                    "passed": False, 
                    "error": str(e)
                 }

        except Exception as e:
            return {"passed": False, "error": str(e)}
