"""插件目录条目的运行态元数据映射。"""

from __future__ import annotations

from collections.abc import Callable
from typing import Any, Optional

from app.runtime.extensions.plugin.contracts import supports_plugin_hook
from app.schemas.plugin import Plugin


class PluginMetadataMapper:
    """把市场或本地仓条目映射为包含运行态状态的插件 DTO。"""

    def __init__(
        self,
        *,
        plugin_instance: Callable[[str], Optional[Any]],
        plugin_class: Callable[[str], Optional[Any]],
        annotate_system_version: Callable[[dict], dict],
        is_package_compatible: Callable[[dict, str], bool],
        auth_checker: Callable[[Plugin, dict], bool],
        version_compare: Callable[[str, str, str], bool],
        log: Any,
    ) -> None:
        """保存注册表、兼容判断和权限判断端口。"""
        self._plugin_instance = plugin_instance
        self._plugin_class = plugin_class
        self._annotate_system_version = annotate_system_version
        self._is_package_compatible = is_package_compatible
        self._auth_checker = auth_checker
        self._version_compare = version_compare
        self._logger = log

    def map(
        self,
        plugin_id: str,
        plugin_info: dict,
        market: str,
        installed_plugins: list[str],
        add_time: int,
        package_version: Optional[str] = None,
    ) -> Optional[Plugin]:
        """映射一个插件索引条目，不兼容或无权限时返回空。"""
        if not isinstance(plugin_info, dict):
            return None
        info = self._annotate_system_version(plugin_info.copy())
        if not self._is_package_compatible(info, package_version or ""):
            return None

        instance = self._plugin_instance(plugin_id)
        plugin_class = self._plugin_class(plugin_id)
        plugin = Plugin(id=plugin_id)
        plugin.installed = plugin_id in installed_plugins and plugin_class is not None
        plugin.has_update = False
        if plugin_class:
            installed_version = getattr(plugin_class, "plugin_version", None)
            online_version = info.get("version")
            if installed_version and online_version:
                plugin.has_update = self._version_compare(
                    installed_version,
                    "<",
                    online_version,
                )

        plugin.system_version = info.get("system_version")
        if info.get("system_version_compatible") is False:
            plugin.system_version_compatible = False
            plugin.system_version_message = info.get("system_version_message")

        plugin.state = self._state(plugin_id, instance)
        plugin.has_page = bool(
            instance and supports_plugin_hook(instance, "get_page")
        )
        if info.get("key"):
            plugin.plugin_public_key = info["key"]
        if not self._auth_checker(plugin, info):
            return None

        plugin.plugin_name = info.get("name")
        plugin.plugin_desc = info.get("description")
        plugin.plugin_version = info.get("version")
        plugin.plugin_icon = info.get("icon")
        plugin.plugin_label = self.normalize_label(info.get("labels"))
        plugin.plugin_author = info.get("author")
        plugin.history = info.get("history") or {}
        plugin.release = bool(info.get("release"))
        plugin.repo_url = market
        plugin.is_local = False
        plugin.add_time = add_time
        return plugin

    def _state(self, plugin_id: str, instance: Optional[Any]) -> bool:
        """安全读取插件运行状态，插件异常时降级为未启用。"""
        if not instance or not hasattr(instance, "get_state"):
            return False
        try:
            return bool(instance.get_state())
        except Exception as error:  # noqa: BLE001
            self._logger.error(f"获取插件 {plugin_id} 状态出错：{error}")
            return False

    @staticmethod
    def normalize_label(labels: Any) -> Optional[str]:
        """兼容市场标签的旧字符串和新列表格式。"""
        if isinstance(labels, str):
            label = labels.strip()
            return label or None
        if isinstance(labels, list):
            normalized = [
                str(item).strip()
                for item in labels
                if str(item).strip()
            ]
            return " ".join(normalized) or None
        return None
