# Runner module for executing AICoder tasks
import asyncio
import json
import os
import sys
from pathlib import Path
from datetime import datetime
from typing import AsyncGenerator, Dict, Any, Optional


class AICoderRunner:
    """Runner for executing AICoder tasks with streaming output"""

    def __init__(self, project_path: str):
        self.project_path = Path(project_path).absolute()
        self.project_path.mkdir(parents=True, exist_ok=True)
        self.running_task = None
        self._cleanup_done = False

    async def run_task_stream(
        self,
        task: str,
        target_project: Optional[str] = None,
        session_id: Optional[str] = None,
    ) -> AsyncGenerator[Dict[str, Any], None]:
        """Run a task and yield events for streaming"""
        if target_project:
            work_dir = Path(target_project).absolute()
        else:
            work_dir = self.project_path

        os.environ["AACODE_WORK_DIR"] = str(self.project_path)

        yield {"type": "start", "task": task, "project": str(work_dir)}

        try:
            if __package__ in (None, ""):
                from aacode.core.main_agent import MainAgent
                from aacode.utils.context_manager import ContextManager
                from aacode.utils.safety import SafetyGuard
                from aacode.config import settings
            else:
                from aacode.core.main_agent import MainAgent
                from aacode.utils.context_manager import ContextManager
                from aacode.utils.safety import SafetyGuard
                from aacode.config import settings

            safety_guard = SafetyGuard(work_dir)
            context_manager = ContextManager(self.project_path)

            agent = MainAgent(
                project_path=work_dir,
                context_manager=context_manager,
                safety_guard=safety_guard,
                model_config=settings.DEFAULT_MODEL,
            )

            if session_id:
                await agent.session_manager.switch_session(session_id)
                yield {"type": "session_switched", "session_id": session_id}
            else:
                session_id = await agent.session_manager.create_session(task)
                yield {"type": "session_created", "session_id": session_id}

            self.running_task = asyncio.current_task()

            result = await agent.execute(task)

            if result.get("status") == "completed":
                yield {"type": "done", "session_id": session_id, "result": result}
            elif result.get("status") == "error":
                yield {"type": "error", "message": result.get("error", result.get("final_thought", "Unknown error"))}
            else:
                yield {"type": "done", "session_id": session_id, "result": result}

        except Exception as e:
            yield {"type": "error", "message": str(e)}

    async def validate_api_key(
        self, api_key: str, model: str = "deepseek-chat", base_url: Optional[str] = None
    ) -> Dict[str, Any]:
        """Validate API key with a simple request"""
        import openai

        os.environ["LLM_API_KEY"] = api_key
        if base_url:
            os.environ["LLM_API_URL"] = base_url
        os.environ["LLM_MODEL_NAME"] = model

        try:
            if base_url:
                client = openai.AsyncOpenAI(api_key=api_key, base_url=base_url)
            else:
                client = openai.AsyncOpenAI(api_key=api_key)

            try:
                await asyncio.wait_for(
                    client.chat.completions.create(
                        model=model,
                        messages=[{"role": "user", "content": "Hi"}],
                        max_tokens=10,
                    ),
                    timeout=10.0,
                )
                return {"valid": True}
            except asyncio.TimeoutError:
                return {"valid": False, "error": "Request timeout"}
            except Exception as e:
                error_msg = str(e).lower()
                if (
                    "api" in error_msg
                    or "key" in error_msg
                    or "auth" in error_msg
                    or "invalid" in error_msg
                ):
                    return {"valid": False, "error": "Invalid API key or model"}
                return {"valid": False, "error": str(e)}
        except Exception as e:
            return {"valid": False, "error": str(e)}

    async def get_session_list(self, project_path: str) -> list:
        """Get list of sessions for a project"""
        session_path = (
            Path(project_path) / ".aacode" / "sessions" / "sessions_index.json"
        )
        if not session_path.exists():
            return []

        try:
            with open(session_path, "r", encoding="utf-8") as f:
                data = json.load(f)
                sessions = []
                for session_id, info in data.items():
                    created_at = info.get("created_at", 0)
                    last_activity = info.get("last_activity", 0)
                    # 确保输出给前端的始终为 ISO 字符串，兼容旧版 float
                    if isinstance(created_at, (int, float)):
                        created_at = datetime.fromtimestamp(created_at).isoformat(timespec='seconds')
                    if isinstance(last_activity, (int, float)):
                        last_activity = datetime.fromtimestamp(last_activity).isoformat(timespec='seconds')
                    sessions.append(
                        {
                            "session_id": session_id,
                            "title": info.get("title", ""),
                            "created_at": created_at,
                            "last_activity": last_activity,
                            "status": info.get("status", "active"),
                        }
                    )
                # 排序：统一转为 float 比较
                def _sort_key(s):
                    ts = s.get("last_activity", "")
                    if isinstance(ts, (int, float)):
                        return float(ts)
                    try:
                        return datetime.fromisoformat(ts).timestamp()
                    except (ValueError, TypeError):
                        return 0.0
                sessions.sort(key=_sort_key, reverse=True)
                return sessions
        except Exception:
            return []

    async def get_session_messages(self, project_path: str, session_id: str) -> list:
        """Get messages for a session"""
        session_file = (
            Path(project_path) / ".aacode" / "sessions" / f"{session_id}.json"
        )
        if not session_file.exists():
            return []

        try:
            with open(session_file, "r", encoding="utf-8") as f:
                data = json.load(f)
                return data.get("messages", [])
        except Exception:
            return []

    async def cleanup(self):
        """Cleanup resources"""
        if not self._cleanup_done:
            self._cleanup_done = True
            if self.running_task and not self.running_task.done():
                self.running_task.cancel()
                try:
                    await self.running_task
                except asyncio.CancelledError:
                    pass
