"""调用上下文：子进程 / 插件拉起 / Host API 注入的 AICD_* 环境变量。"""

from __future__ import annotations

import os
from dataclasses import dataclass

from aicd.config import PluginsConfig


@dataclass(frozen=True)
class InvocationContext:
    """解析一次启动时的环境（根实例 depth=0，子进程由父进程递增）。"""

    depth: int
    spawned_by: str
    parent_plugin_id: str | None
    parent_session: str | None
    host_nonce: str | None
    plugin_policy: str

    def depth_exceeds(self, max_depth: int) -> bool:
        return self.depth > max_depth


def load_invocation_context(
    *,
    plugin_policy_override: str | None = None,
) -> InvocationContext:
    depth_raw = (os.environ.get("AICD_INVOCATION_DEPTH") or "0").strip()
    try:
        depth = max(0, int(depth_raw, 10))
    except ValueError:
        depth = 0

    spawned = (os.environ.get("AICD_SPAWNED_BY") or "user").strip().lower()
    if spawned not in ("user", "plugin", "task", "host_api"):
        spawned = "user"

    pp = (os.environ.get("AICD_PARENT_PLUGIN_ID") or "").strip() or None
    ps = (os.environ.get("AICD_PARENT_SESSION") or "").strip() or None
    hn = (os.environ.get("AICD_HOST_NONCE") or "").strip() or None
    if plugin_policy_override is not None:
        policy = str(plugin_policy_override).strip().lower()
    else:
        policy = (os.environ.get("AICD_PLUGIN_POLICY") or "").strip().lower()

    return InvocationContext(
        depth=depth,
        spawned_by=spawned,
        parent_plugin_id=pp,
        parent_session=ps,
        host_nonce=hn,
        plugin_policy=policy,
    )


def _policy_implies_no_boot(policy: str) -> bool:
    return policy in ("off", "explicit", "no", "none")


def _policy_implies_force_load(policy: str) -> bool:
    return policy in ("full", "on", "all", "yes")


def should_load_plugins_at_boot(
    inv: InvocationContext,
    plugins: PluginsConfig,
    *,
    cli_no_plugins: bool,
) -> tuple[bool, str]:
    """返回 (是否在启动时扫描并加载 HOME 插件, 人类可读原因)。"""

    if cli_no_plugins:
        return False, "cli_no_plugins"

    policy = inv.plugin_policy

    mode = (plugins.boot_plugin_mode or "full").strip().lower()
    if mode not in ("full", "safe", "inherit"):
        mode = "full"

    if mode == "safe":
        return False, "config_boot_plugin_mode_safe"

    if _policy_implies_no_boot(policy):
        return False, f"env_plugin_policy_{policy or 'off'}"

    if _policy_implies_force_load(policy):
        return True, f"env_plugin_policy_{policy}"

    nested = inv.depth > 0 or inv.spawned_by == "plugin"

    if mode == "full":
        if nested:
            return False, "nested_default_safe"
        return True, "boot_plugin_mode_full_root"

    # inherit: 仅 env + 是否嵌套
    if nested:
        return False, "inherit_nested_default_safe"
    return True, "inherit_root_default_full"


def subprocess_env_for_child(
    parent: InvocationContext,
    *,
    spawned_by: str,
    parent_plugin_id: str | None = None,
    host_nonce: str | None = None,
    plugin_policy: str | None = None,
    extra: dict[str, str] | None = None,
) -> dict[str, str]:
    """合并当前 os.environ，并设置子 Aicd 进程的 AICD_* 变量（供 subprocess 使用）。"""

    out = dict(os.environ)
    out["AICD_INVOCATION_DEPTH"] = str(parent.depth + 1)
    sb = spawned_by.strip().lower()
    if sb not in ("user", "plugin", "task", "host_api"):
        sb = "user"
    out["AICD_SPAWNED_BY"] = sb
    if parent_plugin_id and str(parent_plugin_id).strip():
        out["AICD_PARENT_PLUGIN_ID"] = str(parent_plugin_id).strip()
    elif "AICD_PARENT_PLUGIN_ID" in out:
        del out["AICD_PARENT_PLUGIN_ID"]
    if host_nonce and str(host_nonce).strip():
        out["AICD_HOST_NONCE"] = str(host_nonce).strip()
    elif "AICD_HOST_NONCE" in out:
        del out["AICD_HOST_NONCE"]
    if plugin_policy is not None and str(plugin_policy).strip():
        out["AICD_PLUGIN_POLICY"] = str(plugin_policy).strip()
    if extra:
        out.update(extra)
    return out
