"""插件实例生命周期应用能力。"""

from __future__ import annotations

import traceback
from collections.abc import Callable
from functools import wraps
import threading
import time
from typing import Any, Optional, ParamSpec, TypeVar, cast

from app.runtime.observability import record_metric
from app.schemas.plugin import PluginRuntimeStatus


P = ParamSpec("P")
R = TypeVar("R")


def observe_plugin_lifecycle(operation: str) -> Callable[[Callable[P, R]], Callable[P, R]]:
    """为插件生命周期入口记录不含插件标识的低基数耗时。"""

    def decorator(func: Callable[P, R]) -> Callable[P, R]:
        """包装单个同步生命周期方法，并保留原始调用签名。"""

        @wraps(func)
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
            """执行生命周期方法并把失败状态归一为 error。"""
            started_at = time.perf_counter()
            outcome = "success"
            try:
                result = func(*args, **kwargs)
                statuses = result.values() if isinstance(result, dict) else (result,)
                if result is False or PluginRuntimeStatus.LOAD_FAILED in statuses:
                    outcome = "error"
                return result
            except BaseException:
                outcome = "error"
                raise
            finally:
                record_metric(
                    "plugin.lifecycle.duration",
                    time.perf_counter() - started_at,
                    operation=operation,
                    outcome=outcome,
                )

        return cast(Callable[P, R], wrapper)

    return decorator


class PluginLifecycle:
    """管理插件发现、初始化、启停和热重载，不持有市场或 HTTP 路由职责。"""

    _EVENT_HANDLERS_QUIESCED = "__event_handlers__"

    def __init__(
        self,
        *,
        classes: dict[str, Any],
        running: dict[str, Any],
        load_plugins: Callable[[Optional[str], list[str], Callable[[Any], bool]], list[Any]],
        installed_plugins: Callable[[], list[str]],
        plugin_config: Callable[[str], dict],
        auth_checker: Callable[[Any], bool],
        clear_modules: Callable[[Optional[str]], Any],
        clear_tools: Callable[[], None],
        enable_events: Callable[[Any], None],
        disable_events: Callable[[Any], None],
        runtime_status_writer: Callable[[str, PluginRuntimeStatus], None],
        log: Any,
        event_sender: Callable[..., Any],
    ) -> None:
        """保存注册表、加载器和事件端口。"""
        self._classes = classes
        self._running = running
        self._load_plugins = load_plugins
        self._installed_plugins = installed_plugins
        self._plugin_config = plugin_config
        self._auth_checker = auth_checker
        self._clear_modules = clear_modules
        self._clear_tools = clear_tools
        self._enable_events = enable_events
        self._disable_events = disable_events
        self._runtime_status_writer = runtime_status_writer
        self._logger = log
        self._event_sender = event_sender
        self._lifecycle_lock = threading.RLock()
        self._quiesced_hooks: dict[str, set[str]] = {}

    @observe_plugin_lifecycle("start")
    def start(
        self,
        plugin_id: Optional[str] = None,
    ) -> dict[str, PluginRuntimeStatus]:
        """加载并初始化插件，返回每个目标的明确运行结果。"""
        installed_plugins = self._installed_plugins()
        results: dict[str, PluginRuntimeStatus] = {}
        if plugin_id:
            self._runtime_status_writer(plugin_id, PluginRuntimeStatus.READY)

        def check_module(module: Any) -> bool:
            """判断模块是否具备宿主插件最小生命周期钩子。"""
            return hasattr(module, "init_plugin") and hasattr(module, "plugin_name")

        plugins = self._load_plugins(plugin_id, installed_plugins, check_module)
        plugins.sort(key=lambda item: getattr(item, "plugin_order", 0))
        for plugin in plugins:
            current_id = plugin.__name__
            if plugin_id and current_id != plugin_id:
                continue
            try:
                if not self._auth_checker(plugin):
                    if current_id in self._classes:
                        self._classes[current_id] = plugin
                    status = PluginRuntimeStatus.BLOCKED_BY_POLICY
                    self._runtime_status_writer(current_id, status)
                    results[current_id] = status
                    continue
                self._classes[current_id] = plugin
                instance = plugin()
                instance.init_plugin(self._plugin_config(current_id))
                self._quiesced_hooks.pop(current_id, None)
                self._running[current_id] = instance
                self._logger.info(
                    f"加载插件：{current_id} 版本：{instance.plugin_version}"
                )
                if instance.get_state():
                    self._enable_events(plugin)
                else:
                    self._disable_events(plugin)
                status = PluginRuntimeStatus.ACTIVE
                self._runtime_status_writer(current_id, status)
                results[current_id] = status
            except Exception as error:  # noqa: BLE001
                status = PluginRuntimeStatus.LOAD_FAILED
                self._runtime_status_writer(current_id, status)
                results[current_id] = status
                self._logger.error(
                    f"加载插件 {current_id} 出错：{error} - {traceback.format_exc()}"
                )
        if plugin_id and plugin_id not in results:
            status = PluginRuntimeStatus.LOAD_FAILED
            self._runtime_status_writer(plugin_id, status)
            results[plugin_id] = status
        self._clear_tools()
        return results

    @observe_plugin_lifecycle("initialize")
    def initialize(self, plugin_id: str, config: dict) -> None:
        """重新应用指定插件配置并刷新事件注册状态。"""
        plugin = self._running.get(plugin_id)
        if not plugin:
            return
        plugin.init_plugin(config)
        if plugin.get_state():
            self._enable_events(type(plugin))
        else:
            self._disable_events(type(plugin))
        self._clear_tools()

    @observe_plugin_lifecycle("stop")
    def stop(self, plugin_id: Optional[str] = None) -> None:
        """按旧单阶段 ABI 先解绑 handler，再停止并强制卸载插件。"""
        with self._lifecycle_lock:
            plugins = self._select_running_plugins(plugin_id)
            self._quiesce_selected(plugins)
            self._finalize(
                plugin_id,
                require_quiesced=False,
                disable_events=not self._handlers_quiesced(plugins),
            )

    @observe_plugin_lifecycle("quiesce")
    def quiesce(self, plugin_id: Optional[str] = None) -> bool:
        """先解绑事件 handler，再按旧 hook 顺序停止生产者并保留实例。"""
        with self._lifecycle_lock:
            plugins = self._select_running_plugins(plugin_id)
            return self._quiesce_selected(plugins)

    @observe_plugin_lifecycle("quiesce_handlers")
    def quiesce_handlers(self, plugin_id: Optional[str] = None) -> bool:
        """禁止目标插件接收新事件，保留实例供在途 handler 和后续 hook 使用。"""
        with self._lifecycle_lock:
            plugins = self._select_running_plugins(plugin_id)
            return self._disable_selected_handlers(plugins)

    @observe_plugin_lifecycle("quiesce_services")
    def quiesce_services(self, plugin_id: Optional[str] = None) -> bool:
        """在事件结算屏障后执行旧 close、stop_service hook。"""
        with self._lifecycle_lock:
            plugins = self._select_running_plugins(plugin_id)
            if not self._handlers_quiesced(plugins):
                self._logger.warning("插件事件 handler 尚未全部停用，拒绝关闭插件资源")
                return False
            return self._quiesce_hooks(plugins)

    def _quiesce_selected(self, plugins: dict[str, Any]) -> bool:
        """兼容单阶段调用：先停用全部 handler，再执行稳定快照的旧 hooks。"""
        if not self._disable_selected_handlers(plugins):
            return False
        return self._quiesce_hooks(plugins)

    def _disable_selected_handlers(self, plugins: dict[str, Any]) -> bool:
        """先停用稳定快照的全部事件入口，任一失败时不执行破坏性 hook。"""
        all_converged = True
        for current_id, plugin in plugins.items():
            completed = self._quiesced_hooks.setdefault(current_id, set())
            if self._EVENT_HANDLERS_QUIESCED in completed:
                continue
            try:
                self._disable_events(type(plugin))
            except Exception as error:  # noqa: BLE001  插件边界必须隔离
                all_converged = False
                self._logger.warning(
                    f"停用插件 {current_id} 的事件 handler 时发生错误: {error}"
                )
                continue
            completed.add(self._EVENT_HANDLERS_QUIESCED)
        return all_converged

    def _quiesce_hooks(self, plugins: dict[str, Any]) -> bool:
        """执行旧 ABI hooks，并只重试尚未成功的步骤。"""
        all_converged = True
        for current_id, plugin in plugins.items():
            completed = self._quiesced_hooks.setdefault(current_id, set())
            for hook_name in ("close", "stop_service"):
                if hook_name in completed:
                    continue
                hook = getattr(plugin, hook_name, None)
                if not callable(hook):
                    completed.add(hook_name)
                    continue
                try:
                    result = hook()
                except Exception as error:  # noqa: BLE001  插件边界必须隔离
                    all_converged = False
                    self._logger.warning(
                        f"停止插件 {current_id} 的 {hook_name} 时发生错误: {error}"
                    )
                    continue
                if result is False:
                    all_converged = False
                    self._logger.warning(
                        f"停止插件 {current_id} 的 {hook_name} 未收敛"
                    )
                    continue
                completed.add(hook_name)
        return all_converged

    @observe_plugin_lifecycle("finalize")
    def finalize(self, plugin_id: Optional[str] = None) -> bool:
        """在 handler、旧 hook 和事件屏障均收敛后卸载插件实例。"""
        return self._finalize(
            plugin_id,
            require_quiesced=True,
            disable_events=False,
        )

    def _finalize(
        self,
        plugin_id: Optional[str],
        *,
        require_quiesced: bool,
        disable_events: bool = True,
    ) -> bool:
        """按严格或兼容策略卸载插件，并在清理失败时保留实例所有权。"""
        with self._lifecycle_lock:
            plugins = self._select_running_plugins(plugin_id)
            if require_quiesced and any(
                not self._is_quiesced(current_id, plugin)
                for current_id, plugin in plugins.items()
            ):
                self._logger.warning("插件后台服务尚未全部收敛，拒绝卸载运行实例")
                return False

            try:
                if disable_events:
                    for plugin in plugins.values():
                        self._disable_events(type(plugin))
                self._clear_modules(plugin_id)
                self._clear_tools()
            except Exception as error:  # noqa: BLE001  保留实例所有权供后续重试
                self._logger.warning(f"卸载插件运行实例时发生错误: {error}")
                return False

            if plugin_id:
                self._classes.pop(plugin_id, None)
                self._running.pop(plugin_id, None)
                self._quiesced_hooks.pop(plugin_id, None)
            else:
                self._classes.clear()
                self._running.clear()
                self._quiesced_hooks.clear()
            self._logger.info("插件停止完成")
            return True

    def _select_running_plugins(self, plugin_id: Optional[str]) -> dict[str, Any]:
        """返回本阶段处理的稳定实例快照，并保持旧停机日志语义。"""
        if plugin_id:
            self._logger.info(f"正在停止插件 {plugin_id}...")
            plugin = self._running.get(plugin_id)
            plugins = {plugin_id: plugin} if plugin else {}
            if not plugin:
                self._logger.debug(f"插件 {plugin_id} 不存在或未加载")
            return plugins
        self._logger.info("正在停止所有插件...")
        return dict(self._running)

    def _is_quiesced(self, plugin_id: str, plugin: Any) -> bool:
        """判断 handler 及当前实例声明的旧 ABI hooks 是否均已成功收敛。"""
        required = {self._EVENT_HANDLERS_QUIESCED} | {
            hook_name
            for hook_name in ("close", "stop_service")
            if callable(getattr(plugin, hook_name, None))
        }
        return required.issubset(self._quiesced_hooks.get(plugin_id, set()))

    def _handlers_quiesced(self, plugins: dict[str, Any]) -> bool:
        """判断稳定快照中的全部插件是否已经停用事件入口。"""
        return all(
            self._EVENT_HANDLERS_QUIESCED
            in self._quiesced_hooks.get(plugin_id, set())
            for plugin_id in plugins
        )

    @observe_plugin_lifecycle("reload")
    def reload(
        self,
        plugin_id: str,
        reload_event: Any,
    ) -> PluginRuntimeStatus:
        """重启指定插件并返回本次加载结果。"""
        self._runtime_status_writer(plugin_id, PluginRuntimeStatus.READY)
        self.stop(plugin_id)
        status = self.start(plugin_id)[plugin_id]
        self._event_sender(reload_event, data={"plugin_id": plugin_id})
        return status
