# -*- coding: utf-8 -*-
"""验证 ProjectContextService 被正确接入 load_system_prompt 的集成测试。

背景：src/abyss/context/project_context.py 已完整实现 ProjectContextService
（三级缓存、git 探测、文件树收集、5 类项目类型识别），但之前是孤儿模块。
本测试验证 prompts/__init__.py 已正确将其接入系统提示词加载流程：
- {{WORKING_DIR}} 占位符被替换为「cwd + <project_context>...</project_context>」
- 失败时降级为仅 cwd，不阻塞 CLI 启动
- load_system_prompt(project_path=None) 签名向后兼容
"""
import os
from unittest.mock import patch, MagicMock

import pytest

from abyss.prompts import load_system_prompt, _collect_project_context
from abyss.context.project_context import (
    ProjectContext,
    ProjectContextService,
    ProjectType,
)


class TestProjectContextInjection:
    """验证 ProjectContextService 被正确接入 load_system_prompt。"""

    def test_replace_working_dir_without_project_context(self, tmp_path, monkeypatch):
        """_collect_project_context 失败时，{{WORKING_DIR}} 替换为仅 cwd。

        场景：ProjectContextService.get_context 抛异常或返回 None。
        期望：prompt 中包含 cwd 字符串，但不包含 <project_context> 块。
        """
        # mock _collect_project_context 返回空字符串（模拟失败降级）
        monkeypatch.setattr(
            "abyss.prompts._collect_project_context",
            lambda cwd: "",
        )

        prompt = load_system_prompt(str(tmp_path))

        # {{WORKING_DIR}} 已被替换（不再含字面占位符）
        assert "{{WORKING_DIR}}" not in prompt
        # 包含 cwd
        assert str(tmp_path) in prompt
        # 不含项目上下文块（降级路径）
        assert "<project_context>" not in prompt

    def test_replace_working_dir_with_project_context(self, tmp_path):
        """成功获取项目上下文时，{{WORKING_DIR}} 替换为 cwd + 项目上下文。

        场景：tmp_path 是 Python 项目根（含 pyproject.toml）。
        期望：prompt 同时包含 cwd 和 <project_context> 块及 project_type 字段。
        """
        # 在 tmp_path 下创建 pyproject.toml 让项目类型检测命中
        (tmp_path / "pyproject.toml").write_text(
            '[project]\nname = "test"\nversion = "0.1.0"\n',
            encoding="utf-8",
        )

        # 直接调用真实的 _collect_project_context（不 mock）
        prompt = load_system_prompt(str(tmp_path))

        # {{WORKING_DIR}} 已被替换
        assert "{{WORKING_DIR}}" not in prompt
        # 包含 cwd
        assert str(tmp_path) in prompt
        # 包含项目上下文块
        assert "<project_context>" in prompt
        assert "</project_context>" in prompt
        # project_type 应为 python
        assert "project_type: python" in prompt

    def test_load_system_prompt_signature_unchanged(self):
        """load_system_prompt(project_path=None) 签名向后兼容。

        不传参时使用 os.getcwd()，不应报错。
        """
        # 不传 project_path 参数
        prompt = load_system_prompt()
        assert isinstance(prompt, str)
        assert len(prompt) > 0
        # 占位符已替换
        assert "{{WORKING_DIR}}" not in prompt

    def test_collect_project_context_swallows_exception(self, monkeypatch):
        """_collect_project_context 异常时返回空字符串，不抛出。

        场景：ProjectContextService.get_context 抛 RuntimeError。
        期望：_collect_project_context 捕获后返回 ""，不向上传播。
        """
        # 让 get_default_service 抛异常
        def _boom():
            raise RuntimeError("simulated service init failure")

        monkeypatch.setattr(
            "abyss.context.project_context.get_default_service",
            _boom,
        )

        result = _collect_project_context(os.getcwd())
        assert result == ""


class TestProjectContextServiceIntegration:
    """验证 ProjectContextService 单例与 format_project_context 协同。"""

    def test_format_project_context_output_format(self):
        """format_project_context 输出符合 system prompt 注入格式。"""
        ctx = ProjectContext(
            git_root="/path/to/project",
            branch="main",
            head_sha="abcdef1234567890",
            file_tree=["src/main.py", "tests/test_main.py"],
            recent_commits=["abc1234 init", "def5678 add tests"],
            project_type=ProjectType.PYTHON,
            collected_at=1700000000.0,
        )

        text = ProjectContextService.format_project_context(ctx)

        assert text.startswith("<project_context>")
        assert text.endswith("</project_context>")
        assert "git_root: /path/to/project" in text
        assert "branch: main" in text
        assert "head_sha: abcdef12..." in text  # 前 8 位 + ...
        assert "project_type: python" in text
        assert "src/main.py" in text
        assert "abc1234 init" in text

    def test_format_project_context_minimal_context(self):
        """非 git 项目也应有完整的 <project_context> 包裹。"""
        ctx = ProjectContext(
            git_root=None,
            branch=None,
            head_sha=None,
            file_tree=[],
            recent_commits=[],
            project_type=ProjectType.UNKNOWN,
            collected_at=1700000000.0,
        )

        text = ProjectContextService.format_project_context(ctx)

        assert text.startswith("<project_context>")
        assert text.endswith("</project_context>")
        assert "project_type: unknown" in text
        # 非项目字段不应出现（git_root/branch/head_sha 为 None）
        assert "git_root:" not in text
        assert "branch:" not in text
        assert "head_sha:" not in text

    def test_get_default_service_singleton(self):
        """get_default_service 返回全局单例。"""
        from abyss.context.project_context import get_default_service

        s1 = get_default_service()
        s2 = get_default_service()
        assert s1 is s2
