"""Tests for content base rule - 文件内容规则基础类测试."""

from __future__ import annotations

from pathlib import Path

import pytest

from ...models.fileinfo import FileInfo
from ..content._base import BaseContentRule


class MockContentRule(BaseContentRule):
    """用于测试的模拟内容规则."""

    def parse_content(self, fileinfo: FileInfo) -> str:
        """模拟解析内容."""
        # 从测试数据中获取内容
        return getattr(fileinfo, "_test_content", "")


class TestBaseContentRule:
    """BaseContentRule 测试类."""

    @pytest.fixture
    def basic_rule_config(self) -> dict:
        """基础规则配置."""
        return {
            "name": "Test Content Rule",
            "description": "Test description",
            "pattern": "secret",
            "regex": False,
            "case_sensitive": False,
            "context_lines": 3,
        }

    @pytest.fixture
    def regex_rule_config(self) -> dict:
        """正则规则配置."""
        return {
            "name": "Regex Content Rule",
            "description": "Regex pattern test",
            "pattern": r"\b[A-Z][a-z]+\s+[A-Z][a-z]+\b",  # 匹配姓名
            "regex": True,
            "case_sensitive": True,
            "context_lines": 2,
        }

    @pytest.fixture
    def sample_fileinfo(self, tmp_path) -> FileInfo:
        """创建样本文件信息."""
        test_file = tmp_path / "test.txt"
        test_file.write_text("This is a test file with secret content.")
        fileinfo = FileInfo(
            file_path=str(test_file),
            root_path=str(tmp_path),
            file_type=".txt",
            file_size=test_file.stat().st_size,
        )
        # 添加测试内容属性
        fileinfo._test_content = "This is a test file with secret content."
        return fileinfo

    def test_init_with_valid_config(self, basic_rule_config: dict) -> None:
        """测试使用有效配置初始化."""
        rule = MockContentRule(basic_rule_config)
        assert rule.name == "Test Content Rule"
        assert rule.pattern == "secret"
        assert rule.is_regex is False
        assert rule.case_sensitive is False
        assert rule.context_lines == 3
        assert rule.compiled_pattern is None

    def test_init_regex_with_valid_config(self, regex_rule_config: dict) -> None:
        """测试正则表达式初始化."""
        rule = MockContentRule(regex_rule_config)
        assert rule.is_regex is True
        assert rule.compiled_pattern is not None

    def test_init_invalid_regex(self) -> None:
        """测试无效正则表达式处理."""
        config = {
            "name": "Invalid Regex Rule",
            "description": "Invalid regex",
            "pattern": "[invalid(regex",
            "regex": True,
            "case_sensitive": False,
            "context_lines": 3,
        }
        rule = MockContentRule(config)
        assert rule.compiled_pattern is None

    def test_parse_content_abstract(self) -> None:
        """测试 parse_content 是抽象方法."""
        with pytest.raises(TypeError):
            BaseContentRule({})  # type: ignore

    def test_match_basic_text(self, basic_rule_config, tmp_path) -> None:
        """测试基础文本匹配."""
        rule = MockContentRule(basic_rule_config)
        fileinfo = FileInfo(file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))
        fileinfo._test_content = "This file contains secret information."

        result = rule.match(fileinfo)
        assert result is not None
        assert result.rule_name == "Test Content Rule"
        assert result.match_text == "secret"
        assert result.analysis_type == "content"

    def test_match_no_match(self, basic_rule_config, tmp_path) -> None:
        """测试不匹配的情况."""
        rule = MockContentRule(basic_rule_config)
        fileinfo = FileInfo(file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))
        fileinfo._test_content = "This file is clean and has no issues."

        result = rule.match(fileinfo)
        assert result is None

    def test_match_case_sensitive(self, tmp_path: Path) -> None:
        """测试区分大小写的匹配."""
        config = {
            "name": "Case Sensitive Rule",
            "description": "Case sensitive",
            "pattern": "SECRET",
            "regex": False,
            "case_sensitive": True,
            "context_lines": 3,
        }
        rule = MockContentRule(config)

        # 应该匹配
        fileinfo = FileInfo(file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))
        fileinfo._test_content = "This contains SECRET data."
        result = rule.match(fileinfo)
        assert result is not None

        # 不应该匹配
        fileinfo2 = FileInfo(file_path=str(tmp_path / "test2.txt"), root_path=str(tmp_path))
        fileinfo2._test_content = "This contains secret data."
        result2 = rule.match(fileinfo2)
        assert result2 is None

    def test_match_regex(self, regex_rule_config, tmp_path) -> None:
        """测试正则表达式匹配."""
        rule = MockContentRule(regex_rule_config)
        fileinfo = FileInfo(file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))
        fileinfo._test_content = "The meeting was attended by John Smith and Jane Doe."

        result = rule.match(fileinfo)
        assert result is not None
        assert result.match_type == "regex"
        assert result.match_text in {"John Smith", "Jane Doe"}

    def test_find_all_matches_single(self, basic_rule_config, tmp_path) -> None:
        """测试查找所有匹配（单个匹配）."""
        rule = MockContentRule(basic_rule_config)
        fileinfo = FileInfo(file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))
        fileinfo._test_content = "One secret here."

        matches = rule.find_all_matches(fileinfo)
        assert len(matches) == 1
        assert matches[0].match_text == "secret"

    def test_find_all_matches_multiple(self, basic_rule_config, tmp_path) -> None:
        """测试查找所有匹配（多个匹配）."""
        rule = MockContentRule(basic_rule_config)
        fileinfo = FileInfo(file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))
        fileinfo._test_content = "First secret, second secret, and third secret."

        matches = rule.find_all_matches(fileinfo)
        assert len(matches) == 3
        assert all(m.match_text == "secret" for m in matches)

    def test_find_all_matches_with_pre_extracted_text(self, basic_rule_config, tmp_path) -> None:
        """测试使用预提取的文本进行匹配."""
        rule = MockContentRule(basic_rule_config)
        fileinfo = FileInfo(file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))

        # 提供预提取的文本
        text = "Custom secret content"
        matches = rule.find_all_matches(fileinfo, text=text)
        assert len(matches) == 1
        assert matches[0].match_text == "secret"

    def test_find_all_matches_empty_content(self, basic_rule_config, tmp_path) -> None:
        """测试空内容的情况."""
        rule = MockContentRule(basic_rule_config)
        fileinfo = FileInfo(file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))
        fileinfo._test_content = ""

        matches = rule.find_all_matches(fileinfo)
        assert len(matches) == 0

    def test_context_lines_retrieval(self, basic_rule_config, tmp_path) -> None:
        """测试上下文行检索."""
        rule = MockContentRule(basic_rule_config)
        fileinfo = FileInfo(file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))

        content = "Line 1\nLine 2 with secret\nLine 3\nLine 4\nLine 5"
        fileinfo._test_content = content

        matches = rule.find_all_matches(fileinfo)
        assert len(matches) == 1
        match = matches[0]

        # 检查上下文行
        assert match.line_number == 2
        assert len(match.context_lines) > 0
        assert any("secret" in line for line in match.context_lines)

    def test_context_lines_at_boundaries(self, basic_rule_config, tmp_path) -> None:
        """测试边界情况的上下文行检索."""
        rule = MockContentRule(basic_rule_config)
        fileinfo = FileInfo(file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))

        # 第一行匹配
        content = "Secret at start\nLine 2\nLine 3\nLine 4"
        fileinfo._test_content = content

        matches = rule.find_all_matches(fileinfo)
        assert len(matches) == 1
        assert matches[0].line_number == 1
        assert len(matches[0].context_lines) <= 4  # 不会超出边界

    def test_match_info_structure(self, basic_rule_config, tmp_path) -> None:
        """测试 MatchInfo 对象结构."""
        rule = MockContentRule(basic_rule_config)
        fileinfo = FileInfo(id=456, file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))
        fileinfo._test_content = "Contains secret data on this line."

        matches = rule.find_all_matches(fileinfo)
        assert len(matches) == 1
        match = matches[0]

        assert match.file_id == 456
        assert match.rule_name == "Test Content Rule"
        assert match.rule_description == "Test description"
        assert match.match_type == "text"
        assert match.analysis_type == "content"
        assert match.line_number == 1
        assert match.match_text == "secret"
        assert match.start_pos >= 0
        assert match.end_pos > match.start_pos
        assert isinstance(match.context_lines, list)

    def test_regex_with_flags(self, tmp_path: Path) -> None:
        """测试带标志的正则表达式."""
        config = {
            "name": "Case Insensitive Regex",
            "description": "Case insensitive",
            "pattern": r"secret",
            "regex": True,
            "case_sensitive": False,  # 应该启用 IGNORECASE
            "context_lines": 3,
        }
        rule = MockContentRule(config)
        fileinfo = FileInfo(file_path=str(tmp_path / "test.txt"), root_path=str(tmp_path))
        fileinfo._test_content = "This contains SECRET and SeCrEt data."

        matches = rule.find_all_matches(fileinfo)
        assert len(matches) == 2
        assert all(m.match_type == "regex" for m in matches)
