#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------
# Copyright (c) 2026 Huawei Technologies Co., Ltd.
# This file is part of the MindStudio project.
#
# MindStudio is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#
#    http://license.coscl.org.cn/MulanPSL2
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# -------------------------------------------------------------------------

from __future__ import annotations

from pathlib import Path
from types import SimpleNamespace
from unittest.mock import AsyncMock

import pytest

from msagent.agents.context import AgentContext
from msagent.cli.bootstrap.initializer import Initializer
from msagent.configs import ToolApprovalConfig
from msagent.skills.factory import Skill


class _DummyAsyncContext:
    async def __aenter__(self):
        return object()

    async def __aexit__(self, exc_type, exc, tb):
        return None


class _DummyMcpClient:
    async def tools(self):
        return []

    async def close(self):
        return None


def test_initializer_resolves_cached_mcp_server_names_from_filtered_tools() -> None:
    init = Initializer()
    mcp_config = SimpleNamespace(
        servers={
            "msprof-mcp": SimpleNamespace(enabled=True),
            "other-mcp": SimpleNamespace(enabled=True),
        }
    )
    tools = [
        SimpleNamespace(name="msprof-mcp_ping"),
        SimpleNamespace(name="get_skill"),
    ]

    resolved = init._resolve_cached_mcp_server_names(
        tools=tools,
        mcp_config=mcp_config,
        mcp_module_map={"msprof-mcp_ping": "mcp:msprof-mcp"},
    )

    assert resolved == ["msprof-mcp"]


@pytest.mark.asyncio
async def test_initializer_passes_agent_context_schema_to_agent_factory(
    tmp_path: Path,
    monkeypatch,
) -> None:
    init = Initializer()

    agent_config = SimpleNamespace(
        checkpointer=None,
        tools=None,
        llm=SimpleNamespace(),
        name="msagent",
        prompt="prompt",
    )
    mcp_config = SimpleNamespace(servers={})
    registry = SimpleNamespace(
        get_agent=AsyncMock(return_value=agent_config),
        load_mcp=AsyncMock(return_value=mcp_config),
        load_approval=lambda: ToolApprovalConfig.model_validate(
            {"interrupt_on": {"execute": {"allowed_decisions": ["approve", "reject"]}}}
        ),
    )
    monkeypatch.setattr(init, "get_registry", lambda _wd: registry)

    monkeypatch.setattr(init, "_create_checkpointer", lambda *_args, **_kwargs: _DummyAsyncContext())
    monkeypatch.setattr(init.mcp_factory, "create", AsyncMock(return_value=_DummyMcpClient()))

    fake_skill = Skill(
        name="demo",
        description="demo skill",
        category="default",
        path=tmp_path / "skills" / "demo" / "SKILL.md",
    )
    monkeypatch.setattr(
        init.skill_factory,
        "load_skills",
        AsyncMock(return_value={"default": {"demo": fake_skill}}),
    )

    fake_graph = SimpleNamespace(_llm_tools=[], _tools_in_catalog=[])
    create_mock = AsyncMock(return_value=fake_graph)
    monkeypatch.setattr(init.agent_factory, "create", create_mock)

    _graph, cleanup = await init.create_graph(
        agent=None,
        model=None,
        working_dir=tmp_path,
    )

    assert create_mock.await_args.kwargs["context_schema"] is AgentContext
    assert create_mock.await_args.kwargs["skills_dir"] is None
    assert create_mock.await_args.kwargs["interrupt_on"] == {"execute": {"allowed_decisions": ["approve", "reject"]}}
    await cleanup()


def test_initializer_filters_skills_by_patterns_with_negative_rules(
    tmp_path: Path,
) -> None:
    keep = Skill(
        name="ascend-profiler-data-validation",
        description="keep",
        category="default",
        path=tmp_path / "skills" / "ascend-profiler-data-validation" / "SKILL.md",
    )
    drop = Skill(
        name="op-mfu-calculator",
        description="drop",
        category="default",
        path=tmp_path / "skills" / "op-mfu-calculator" / "SKILL.md",
    )
    other_category = Skill(
        name="my-skill",
        description="other",
        category="analysis",
        path=tmp_path / "skills" / "analysis" / "my-skill" / "SKILL.md",
    )

    filtered = Initializer._filter_skills_by_patterns(
        [keep, drop, other_category],
        patterns=[
            "default:*",
            "!default:op-mfu-calculator",
        ],
    )

    assert [skill.name for skill in filtered] == ["ascend-profiler-data-validation"]