# -*- coding: utf-8 -*-
"""CM AiAnalysisCommands 测试（Phase 8 Tier 2）

借鉴 zhikuncode AiAnalysisCommands.java：
- /bug-hunter (别名 /bughunt) — 智能扫描项目潜在 Bug
- /pr-review (别名 /review-pr) — 审查 Git diff 或指定 PR
- /security-scan (别名 /sec-scan) — 扫描安全漏洞
- /explain — 解释代码片段或概念

实现模式：复用 _inject_methodology，生成结构化 prompt 注入 session。
"""
from unittest.mock import MagicMock, patch

import pytest

from abyss.commands.ai_analysis import (
    handle_ai_analysis,
    AI_ANALYSIS_COMMANDS,
    BUG_HUNTER_PROMPT,
    PR_REVIEW_PROMPT,
    SECURITY_SCAN_PROMPT,
    EXPLAIN_PROMPT,
    ALIASES,
)


class TestCommandRegistry:
    """命令注册表测试"""

    def test_bug_hunter_registered(self):
        assert "/bug-hunter" in AI_ANALYSIS_COMMANDS

    def test_pr_review_registered(self):
        assert "/pr-review" in AI_ANALYSIS_COMMANDS

    def test_security_scan_registered(self):
        assert "/security-scan" in AI_ANALYSIS_COMMANDS

    def test_explain_registered(self):
        assert "/explain" in AI_ANALYSIS_COMMANDS

    def test_aliases_map_bughunt_to_bug_hunter(self):
        assert ALIASES["/bughunt"] == "/bug-hunter"

    def test_aliases_map_review_pr_to_pr_review(self):
        assert ALIASES["/review-pr"] == "/pr-review"

    def test_aliases_map_sec_scan_to_security_scan(self):
        assert ALIASES["/sec-scan"] == "/security-scan"

    def test_all_commands_have_prompt(self):
        for cmd in AI_ANALYSIS_COMMANDS:
            assert AI_ANALYSIS_COMMANDS[cmd] is not None
            assert isinstance(AI_ANALYSIS_COMMANDS[cmd], str)


class TestHandleAiAnalysis:
    """handle_ai_analysis 测试"""

    def test_bug_hunter_injects_prompt(self):
        """bug-hunter 注入 prompt 到 session"""
        session = MagicMock()
        result = handle_ai_analysis("/bug-hunter", "scan my project", session, ".")
        assert result == "injected"
        session.add_user.assert_called_once()
        args = session.add_user.call_args[0]
        assert "scan my project" in args[0]
        assert BUG_HUNTER_PROMPT in args[0] or "Bug" in args[0] or "bug" in args[0]

    def test_bug_hunter_alias_bughunt_works(self):
        """别名 /bughunt 等同于 /bug-hunter"""
        session = MagicMock()
        # 模拟 main.py 中的别名解析
        cmd = ALIASES.get("/bughunt", "/bughunt")
        result = handle_ai_analysis(cmd, "find bugs", session, ".")
        assert result == "injected"

    def test_pr_review_injects_prompt(self):
        """pr-review 注入 prompt"""
        session = MagicMock()
        result = handle_ai_analysis("/pr-review", "", session, ".")
        assert result == "injected"
        args = session.add_user.call_args[0]
        assert "PR" in args[0] or "pr" in args[0] or "review" in args[0].lower()

    def test_pr_review_alias_works(self):
        """别名 /review-pr 等同于 /pr-review"""
        session = MagicMock()
        cmd = ALIASES.get("/review-pr", "/review-pr")
        result = handle_ai_analysis(cmd, "", session, ".")
        assert result == "injected"

    def test_security_scan_includes_owasp_top_10(self):
        """security-scan 包含 OWASP Top 10 类别"""
        session = MagicMock()
        result = handle_ai_analysis("/security-scan", "", session, ".")
        assert result == "injected"
        args = session.add_user.call_args[0]
        # 至少提及常见 OWASP 类别
        prompt_text = args[0].lower()
        assert "owasp" in prompt_text or "sql" in prompt_text or "xss" in prompt_text or "inject" in prompt_text

    def test_security_scan_alias_works(self):
        """别名 /sec-scan 等同于 /security-scan"""
        session = MagicMock()
        cmd = ALIASES.get("/sec-scan", "/sec-scan")
        result = handle_ai_analysis(cmd, "", session, ".")
        assert result == "injected"

    def test_explain_with_target(self):
        """explain 带目标说明"""
        session = MagicMock()
        result = handle_ai_analysis("/explain", "what is async/await", session, ".")
        assert result == "injected"
        args = session.add_user.call_args[0]
        assert "async/await" in args[0]

    def test_explain_without_target_prompts_user(self):
        """explain 无目标时提示用户"""
        session = MagicMock()
        result = handle_ai_analysis("/explain", "", session, ".")
        # 无目标时返回错误提示，不注入
        assert result != "injected"
        assert "用法" in result or "usage" in Result_lower(result) if isinstance(result, str) else True

    def test_unknown_command_returns_error(self):
        """未知命令返回错误"""
        session = MagicMock()
        result = handle_ai_analysis("/unknown-cmd", "", session, ".")
        assert "未知" in result or "unknown" in result.lower()
        session.add_user.assert_not_called()


def Result_lower(s: str) -> str:
    return s.lower()


class TestPromptContent:
    """prompt 内容质量测试"""

    def test_bug_hunter_prompt_includes_categories(self):
        """bug-hunter prompt 包含 bug 类别"""
        assert "空指针" in BUG_HUNTER_PROMPT or "null" in BUG_HUNTER_PROMPT.lower() or "资源泄漏" in BUG_HUNTER_PROMPT or "memory" in BUG_HUNTER_PROMPT.lower() or "并发" in BUG_HUNTER_PROMPT or "race" in BUG_HUNTER_PROMPT.lower()

    def test_pr_review_prompt_includes_review_aspects(self):
        """pr-review prompt 包含评审维度"""
        assert "代码规范" in PR_REVIEW_PROMPT or "style" in PR_REVIEW_PROMPT.lower() or "可读性" in PR_REVIEW_PROMPT or "readab" in PR_REVIEW_PROMPT.lower()

    def test_security_scan_prompt_includes_owasp(self):
        """security-scan prompt 包含 OWASP"""
        assert "OWASP" in SECURITY_SCAN_PROMPT or "owasp" in SECURITY_SCAN_PROMPT.lower()

    def test_explain_prompt_includes_explanation_style(self):
        """explain prompt 包含解释风格指引"""
        assert "解释" in EXPLAIN_PROMPT or "explain" in EXPLAIN_PROMPT.lower() or "说明" in EXPLAIN_PROMPT
