"""
Auto-Coder SDK 桥接层

连接 SDK 和底层 auto_coder_runner 功能
"""

import os
import sys
from typing import Any, Dict, Optional, Iterator, Union
from pathlib import Path
import subprocess
from contextlib import contextmanager

from ..exceptions import BridgeError
from ..models.responses import StreamEvent
from autocoder.auto_coder_runner import run_auto_command, configure
from ..models.options import AutoCodeOptions

# 不导入事件类型，使用动态检查避免类型冲突


@contextmanager
def temporary_chdir(path: Union[str, Path]):
    """临时切换工作目录的上下文管理器"""
    original_cwd = os.getcwd()
    try:
        os.chdir(path)
        yield
    finally:
        os.chdir(original_cwd)


class AutoCoderBridge:
    """桥接层，连接现有功能"""

    def __init__(self, project_root: str, options: AutoCodeOptions):
        """
        初始化桥接层

        Args:
            project_root: 项目根目录
            options: 配置选项
        """
        self.project_root = project_root or os.getcwd()
        self.options = options
        self.allow_chdir = True  # 默认允许切换目录，可以通过配置关闭
        self._setup_environment()

    def _setup_environment(self):
        """设置环境和内存"""
        try:
            # 使用上下文管理器临时切换到项目根目录
            if self.allow_chdir:
                with temporary_chdir(self.project_root):
                    self._init_project_environment()
            else:
                # 不切换目录的情况下初始化
                self._init_project_environment()

        except Exception as e:
            # 设置环境失败不应该阻止程序运行
            pass

    def _init_project_environment(self):
        """初始化项目环境"""
        try:
            self.init_project_if_required()
            from autocoder.auto_coder_runner import start

            start()
        except ImportError:
            # 如果无法导入，跳过初始化
            pass

    def init_project_if_required(self):
        from autocoder.auto_coder_runner import (
            init_project_if_required as init_project_if_required_buildin,
        )

        init_project_if_required_buildin(self.project_root, "*")

    def call_run_auto_command(
        self,
        query: str,
        pre_commit: bool = False,
        pr: bool = False,
        cancel_token: Optional[str] = None,
        extra_args: Optional[Dict[str, Any]] = None,
        stream: bool = True,
    ) -> Iterator[StreamEvent]:
        """
        调用 run_auto_command 功能并返回事件流

        Args:
            query: 查询内容
            pre_commit: 是否预提交
            pr: 是否预提交
            cancel_token: 取消令牌，用于中断执行
            extra_args: 额外参数
            stream: 是否流式返回

        Yields:
            StreamEvent: 事件流

        Raises:
            BridgeError: 桥接层错误
        """
        try:
            # 准备参数
            extra_args = extra_args or {}

            # 根据会话配置确定 conversation_action 和 conversation_id
            from autocoder.common.v2.agent.agentic_edit_types import ConversationAction

            conversation_action = ConversationAction.NEW  # 默认为新对话
            conversation_id = None

            if self.options.session_id:
                # 如果有 session_id，就是恢复特定会话
                conversation_action = ConversationAction.RESUME
                conversation_id = self.options.session_id
            elif self.options.continue_session:
                # 如果是 continue_session，就是继续最近的对话
                conversation_action = ConversationAction.CONTINUE
                conversation_id = None

            # 发送开始事件
            yield StreamEvent(
                event_type="start",
                data={
                    "query": query,
                    "pre_commit": pre_commit,
                    "cancel_token": cancel_token,
                    "conversation_action": conversation_action.value,
                    "conversation_id": conversation_id,
                },
            )

            # 使用上下文管理器切换目录（如果允许）
            context = (
                temporary_chdir(self.project_root)
                if self.allow_chdir
                else _nullcontext()
            )

            with context:
                try:
                    # 实现循环逻辑
                    loop_count = self.options.loop

                    for loop_index in range(loop_count):
                        # 第一次使用原始查询，后续循环使用改进的查询
                        current_query = query
                        if loop_index > 0:
                            # 使用自定义的额外提示或默认提示
                            if self.options.loop_additional_prompt:
                                additional_prompt = self.options.loop_additional_prompt
                            else:
                                additional_prompt = "use git log to get the code changes generated by previous tasks and try to focus on iterative improvements and refinements. Ensure the implementation is complete, functional, and fully usable without any missing features or incomplete functionality. Make sure to use git commit command to make a commit after every single file edit."
                            current_query = f"{query}\n\nAdditional instruction: {additional_prompt}"

                        # 发送循环开始事件
                        if loop_count > 1:
                            yield StreamEvent(
                                event_type="loop_start",
                                data={"content": f"{loop_index + 1}/{loop_count}"},
                            )

                        # 对于循环执行，根据loop_keep_conversation参数决定是否使用CONTINUE模式
                        current_conversation_action = conversation_action
                        current_conversation_id = conversation_id
                        if loop_index > 0 and self.options.loop_keep_conversation:
                            from autocoder.common.v2.agent.agentic_edit_types import (
                                ConversationAction,
                            )

                            current_conversation_action = ConversationAction.CONTINUE
                            current_conversation_id = None

                        events = run_auto_command(
                            query=current_query,
                            pre_commit=pre_commit,
                            pr=pr
                            and (
                                loop_index == loop_count - 1
                            ),  # 只在最后一次循环时创建PR
                            cancel_token=cancel_token,
                            extra_args={
                                **extra_args,
                                "max_turns": self.options.max_turns,
                                "model": self.options.model,
                                "include_rules": self.options.include_rules,
                            },
                            system_prompt=self.options.system_prompt,
                            conversation_action=current_conversation_action,
                            conversation_id=current_conversation_id,
                            is_sub_agent=self.options.is_sub_agent,
                        )

                        # 如果返回的是生成器，逐个处理事件
                        if hasattr(events, "__iter__") and not isinstance(
                            events, (str, bytes)
                        ):
                            for event in events:
                                # 转换事件格式
                                stream_event = self._convert_event_to_stream_event(
                                    event
                                )
                                if stream_event:  # 只yield非None的事件
                                    yield stream_event
                        else:
                            # 如果不是生成器，包装成单个事件
                            yield StreamEvent(
                                event_type="content", data={"content": str(events)}
                            )

                        # 发送循环完成事件
                        if loop_count > 1:
                            yield StreamEvent(
                                event_type="loop_end",
                                data={"content": f"{loop_index + 1}/{loop_count}"},
                            )

                except (ImportError, FileNotFoundError, Exception) as e:
                    raise BridgeError(
                        f"run_auto_command failed: {str(e)}", original_error=e
                    )

            # 发送完成事件
            yield StreamEvent(event_type="end", data={"status": "completed"})

        except Exception as e:
            # 发送错误事件
            yield StreamEvent(
                event_type="error",
                data={"error": str(e), "error_type": type(e).__name__},
            )
            raise BridgeError(f"run_auto_command failed: {str(e)}", original_error=e)

    def _simulate_auto_command_response(self, query: str) -> Iterator[StreamEvent]:
        """
        模拟 auto_command 响应

        Args:
            query: 查询内容

        Yields:
            StreamEvent: 模拟的事件流
        """
        yield StreamEvent(
            event_type="content", data={"content": f"[模拟模式] 正在处理查询: {query}"}
        )
        yield StreamEvent(
            event_type="content",
            data={
                "content": f"[模拟模式] 这是对您查询的模拟响应。在真实环境中，Auto-Coder 会："
            },
        )

        # 根据查询内容生成合适的模拟响应
        if any(
            keyword in query.lower()
            for keyword in ["function", "函数", "write", "create", "写"]
        ):
            yield StreamEvent(event_type="content", data={"content": "1. 分析您的需求"})
            yield StreamEvent(
                event_type="content", data={"content": "2. 生成相应的代码"}
            )
            yield StreamEvent(
                event_type="content", data={"content": "3. 添加适当的注释和文档"}
            )
        elif any(
            keyword in query.lower()
            for keyword in ["error", "错误", "handling", "处理"]
        ):
            yield StreamEvent(
                event_type="content", data={"content": "1. 识别潜在的错误点"}
            )
            yield StreamEvent(
                event_type="content", data={"content": "2. 添加 try-catch 块"}
            )
            yield StreamEvent(
                event_type="content", data={"content": "3. 添加适当的日志记录"}
            )
        elif any(
            keyword in query.lower() for keyword in ["explain", "解释", "how", "如何"]
        ):
            yield StreamEvent(event_type="content", data={"content": "1. 分析代码结构"})
            yield StreamEvent(
                event_type="content", data={"content": "2. 提供详细的解释"}
            )
            yield StreamEvent(event_type="content", data={"content": "3. 给出使用示例"})
        else:
            yield StreamEvent(event_type="content", data={"content": "1. 理解您的请求"})
            yield StreamEvent(
                event_type="content", data={"content": "2. 执行相应的操作"}
            )
            yield StreamEvent(event_type="content", data={"content": "3. 返回结果"})

        yield StreamEvent(
            event_type="content",
            data={
                "content": "\n注意：当前运行在模拟模式下。要使用完整功能，请确保正确安装 Auto-Coder 核心组件。"
            },
        )

    def _convert_event_to_stream_event(self, event: Any) -> Optional[StreamEvent]:
        """
        将 run_auto_command 的事件转换为 StreamEvent

        Args:
            event: 原始事件

        Returns:
            StreamEvent: 转换后的事件，如果不需要转换则返回 None
        """
        try:
            # 获取事件类型名称
            event_class_name = type(event).__name__

            # 根据事件类型名称进行转换
            if event_class_name == "LLMThinkingEvent":
                return StreamEvent(
                    event_type="llm_thinking", data={"text": getattr(event, "text", "")}
                )

            elif event_class_name == "LLMOutputEvent":
                return StreamEvent(
                    event_type="llm_output", data={"text": getattr(event, "text", "")}
                )

            elif event_class_name == "ToolCallEvent":
                tool_name = "Unknown"
                tool_args = {}
                tool_xml = ""

                if hasattr(event, "tool"):
                    tool_name = (
                        type(event.tool).__name__
                        if hasattr(event.tool, "__class__")
                        else "Unknown"
                    )
                    if hasattr(event.tool, "model_dump"):
                        try:
                            tool_args = event.tool.model_dump()
                        except:
                            tool_args = {}
                    elif hasattr(event.tool, "__dict__"):
                        tool_args = event.tool.__dict__

                if hasattr(event, "tool_xml"):
                    tool_xml = event.tool_xml

                return StreamEvent(
                    event_type="tool_call",
                    data={
                        "tool_name": tool_name,
                        "args": tool_args,
                        "tool_xml": tool_xml,
                    },
                )

            elif event_class_name == "ToolResultEvent":
                result_data = {
                    "tool_name": getattr(event, "tool_name", "Unknown"),
                    "success": True,
                    "message": "",
                    "content": None,
                }

                if hasattr(event, "result") and event.result:
                    if hasattr(event.result, "success"):
                        result_data["success"] = event.result.success
                    if hasattr(event.result, "message"):
                        result_data["message"] = event.result.message
                    if hasattr(event.result, "content"):
                        result_data["content"] = event.result.content

                return StreamEvent(event_type="tool_result", data=result_data)

            elif event_class_name == "CompletionEvent":
                result = ""
                if hasattr(event, "completion"):
                    if hasattr(event.completion, "result"):
                        result = event.completion.result
                    elif hasattr(event.completion, "response"):
                        result = event.completion.response

                return StreamEvent(event_type="completion", data={"result": result})

            elif event_class_name == "ErrorEvent":
                return StreamEvent(
                    event_type="error",
                    data={
                        "error": getattr(event, "message", "Unknown error"),
                        "error_type": "AgenticError",
                    },
                )

            elif event_class_name == "TokenUsageEvent":
                usage = getattr(event, "usage", None)
                if usage:
                    # 序列化 SingleOutputMeta 对象
                    usage_data = {
                        "input_tokens_count": getattr(usage, "input_tokens_count", 0),
                        "generated_tokens_count": getattr(
                            usage, "generated_tokens_count", 0
                        ),
                        "reasoning_content": getattr(usage, "reasoning_content", ""),
                        "finish_reason": getattr(usage, "finish_reason", "stop"),
                        "first_token_time": getattr(usage, "first_token_time", 0),
                    }
                else:
                    usage_data = {}

                return StreamEvent(event_type="token_usage", data={"usage": usage_data})

            elif event_class_name == "WindowLengthChangeEvent":
                return StreamEvent(
                    event_type="window_change",
                    data={"tokens_used": getattr(event, "tokens_used", 0)},
                )

            elif event_class_name == "ConversationIdEvent":
                return StreamEvent(
                    event_type="conversation_id",
                    data={"conversation_id": getattr(event, "conversation_id", "")},
                )

            elif event_class_name == "PreCommitEvent":
                # event.commit_result 和 event.tpe 一定存在
                commit_result = event.commit_result
                commit_data = {
                    "success": (
                        commit_result.success
                        if hasattr(commit_result, "success")
                        else False
                    ),
                    "message": (
                        commit_result.message
                        if hasattr(commit_result, "message")
                        else ""
                    ),
                    "commit_hash": (
                        commit_result.commit_hash
                        if hasattr(commit_result, "commit_hash")
                        else ""
                    ),
                    "files_changed": (
                        commit_result.files_changed
                        if hasattr(commit_result, "files_changed")
                        else []
                    ),
                    "error": (
                        commit_result.error if hasattr(commit_result, "error") else None
                    ),
                }

                return StreamEvent(
                    event_type="pre_commit",
                    data={"commit_result": commit_data, "tpe": event.tpe},
                )

            elif event_class_name == "CommitEvent":
                # event.commit_result 和 event.tpe 一定存在
                commit_result = event.commit_result
                commit_data = {
                    "success": (
                        commit_result.success
                        if hasattr(commit_result, "success")
                        else False
                    ),
                    "message": (
                        commit_result.message
                        if hasattr(commit_result, "message")
                        else ""
                    ),
                    "commit_hash": (
                        commit_result.commit_hash
                        if hasattr(commit_result, "commit_hash")
                        else ""
                    ),
                    "files_changed": (
                        commit_result.files_changed
                        if hasattr(commit_result, "files_changed")
                        else []
                    ),
                    "error": (
                        commit_result.error if hasattr(commit_result, "error") else None
                    ),
                }

                return StreamEvent(
                    event_type="commit",
                    data={"commit_result": commit_data, "tpe": event.tpe},
                )

            elif event_class_name == "PullRequestEvent":
                # event.pull_request_result 一定存在
                pr_result = event.pull_request_result
                pr_data = {
                    "success": (
                        pr_result.success if hasattr(pr_result, "success") else False
                    ),
                    "pr_number": (
                        pr_result.pr_number if hasattr(pr_result, "pr_number") else None
                    ),
                    "pr_url": pr_result.pr_url if hasattr(pr_result, "pr_url") else "",
                    "pr_id": pr_result.pr_id if hasattr(pr_result, "pr_id") else None,
                    "error_message": (
                        pr_result.error_message
                        if hasattr(pr_result, "error_message")
                        else None
                    ),
                    "error_code": (
                        pr_result.error_code
                        if hasattr(pr_result, "error_code")
                        else None
                    ),
                    "platform": (
                        pr_result.platform if hasattr(pr_result, "platform") else None
                    ),
                    "raw_response": (
                        pr_result.raw_response
                        if hasattr(pr_result, "raw_response")
                        else None
                    ),
                    "retry_after": (
                        pr_result.retry_after
                        if hasattr(pr_result, "retry_after")
                        else None
                    ),
                }

                return StreamEvent(
                    event_type="pull_request", data={"pull_request_result": pr_data}
                )

            elif event_class_name == "PlanModeRespondEvent":
                result = ""
                if hasattr(event, "completion"):
                    if hasattr(event.completion, "response"):
                        result = event.completion.response

                return StreamEvent(
                    event_type="plan_mode_respond", data={"result": result}
                )

            # 处理简单字符串或其他类型
            elif isinstance(event, str):
                return StreamEvent(event_type="content", data={"content": event})

            # 如果有event_type属性，尝试直接使用
            elif hasattr(event, "event_type"):
                return StreamEvent(
                    event_type=event.event_type,
                    data=getattr(event, "data", {}),
                    timestamp=getattr(event, "timestamp", None),
                )

            # 未知事件类型，包装成内容事件
            else:
                return StreamEvent(event_type="content", data={"content": str(event)})

        except Exception as e:
            # 转换失败，返回错误事件
            return StreamEvent(
                event_type="content",
                data={"content": f"[Event Conversion Error: {str(e)}]"},
            )

    def get_memory(self) -> Dict[str, Any]:
        """
        获取当前内存状态

        Returns:
            Dict[str, Any]: 内存数据
        """
        try:
            if self.allow_chdir:
                with temporary_chdir(self.project_root):
                    return self._get_memory_internal()
            else:
                return self._get_memory_internal()
        except Exception as e:
            raise BridgeError(f"Failed to get memory: {str(e)}", original_error=e)

    def _get_memory_internal(self) -> Dict[str, Any]:
        """内部获取内存实现"""
        try:
            from autocoder.auto_coder_runner import get_memory

            memory_data = get_memory()
            return memory_data
        except ImportError:
            # 模拟内存数据
            return {
                "current_files": {"files": [], "groups": {}},
                "conf": {},
                "exclude_dirs": [],
                "mode": "auto_detect",
            }

    def save_memory(self, memory_data: Dict[str, Any]) -> None:
        """
        保存内存状态

        Args:
            memory_data: 要保存的内存数据
        """
        try:
            if self.allow_chdir:
                with temporary_chdir(self.project_root):
                    self._save_memory_internal(memory_data)
            else:
                self._save_memory_internal(memory_data)
        except Exception as e:
            raise BridgeError(f"Failed to save memory: {str(e)}", original_error=e)

    def _save_memory_internal(self, memory_data: Dict[str, Any]) -> None:
        """内部保存内存实现"""
        try:
            from autocoder.auto_coder_runner import save_memory, memory

            # 更新全局 memory 变量
            memory.update(memory_data)
            save_memory()
        except ImportError:
            # 模拟保存
            pass

    def get_project_config(self) -> Dict[str, Any]:
        """
        获取项目配置

        Returns:
            Dict[str, Any]: 项目配置
        """
        try:
            if self.allow_chdir:
                with temporary_chdir(self.project_root):
                    return self._get_project_config_internal()
            else:
                return self._get_project_config_internal()
        except Exception as e:
            raise BridgeError(
                f"Failed to get project config: {str(e)}", original_error=e
            )

    def _get_project_config_internal(self) -> Dict[str, Any]:
        """内部获取配置实现"""
        try:
            from autocoder.auto_coder_runner import get_final_config

            config = get_final_config()
            return config.__dict__ if hasattr(config, "__dict__") else {}
        except ImportError:
            # 模拟配置
            return {"mock_config": True}

    def setup_project_context(self) -> None:
        """设置项目上下文"""
        try:
            # 确保项目环境已正确初始化
            self._setup_environment()
        except Exception as e:
            raise BridgeError(
                f"Failed to setup project context: {str(e)}", original_error=e
            )

    def cleanup(self) -> None:
        """清理资源"""
        try:
            if self.allow_chdir:
                with temporary_chdir(self.project_root):
                    self._cleanup_internal()
            else:
                self._cleanup_internal()
        except Exception as e:
            # 清理失败不应该阻止程序继续运行，只记录错误
            print(f"Warning: Failed to cleanup resources: {str(e)}")

    def _cleanup_internal(self) -> None:
        """内部清理实现"""
        try:
            from autocoder.auto_coder_runner import stop

            stop()
        except ImportError:
            pass

    def configure_agentic_max_rounds(self, max_rounds: int = 10000) -> None:
        """
        配置代理最大轮数

        Args:
            max_rounds: 最大轮数，默认为10000
        """
        try:
            configure(f"agentic_max_rounds:{max_rounds}", skip_print=True)
        except Exception as e:
            raise BridgeError(
                f"Failed to configure agentic max rounds: {str(e)}", original_error=e
            )

    def configure_model(self, model_name: str) -> None:
        """
        配置模型
        """
        try:
            configure(f"model:{model_name}", skip_print=True)
        except Exception as e:
            raise BridgeError(
                f"Failed to configure model '{model_name}': {str(e)}", original_error=e
            )

    def run_workflow(
        self,
        workflow: str,
        prompt: Optional[str],
        model: Optional[str],
        vars_override: Optional[Dict[str, Any]] = None,
    ) -> Any:
        """
        运行指定的 workflow

        Args:
            workflow: workflow 名称或文件路径
            prompt: 可选的提示内容，会注入到 workflow 的 vars.query 中
            model: 可选的模型名称，覆盖 workflow 配置中的模型
            vars_override: 可选的变量覆盖字典

        Returns:
            WorkflowResult: workflow 执行结果

        Raises:
            BridgeError: 桥接层错误
        """
        try:
            # 使用上下文管理器切换目录（如果允许）
            context = (
                temporary_chdir(self.project_root)
                if self.allow_chdir
                else _nullcontext()
            )

            with context:
                try:
                    from autocoder.auto_coder_runner import (
                        run_workflow as _run_workflow,
                    )

                    return _run_workflow(
                        workflow=workflow,
                        prompt=prompt,
                        model=model,
                        vars_override=vars_override,
                    )
                except (ImportError, FileNotFoundError, Exception) as e:
                    raise BridgeError(
                        f"run_workflow failed: {str(e)}", original_error=e
                    )

        except Exception as e:
            raise BridgeError(f"run_workflow failed: {str(e)}", original_error=e)

    def __enter__(self):
        """上下文管理器入口"""
        self.setup_project_context()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """上下文管理器出口"""
        self.cleanup()


class _nullcontext:
    """Python 3.7 兼容的 nullcontext"""

    def __enter__(self):
        return self

    def __exit__(self, *args):
        return False
