"""Tests for zipfile rule - 压缩包规则测试."""

from __future__ import annotations

import zipfile
from pathlib import Path

import pytest

from ...models.fileinfo import FileInfo
from ..content.zipfile import ZipfileRule


class TestZipfileRule:
    """ZipfileRule 测试类."""

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

    @pytest.fixture
    def regex_rule_config(self) -> dict:
        """正则规则配置."""
        return {
            "name": "Regex Zipfile Rule",
            "description": "Regex pattern test",
            "pattern": r"\.exe",  # 匹配 exe 文件（不要求行尾）
            "regex": True,
            "case_sensitive": False,
            "context_lines": 2,
        }

    @pytest.fixture
    def zip_file_with_content(self, tmp_path) -> Path:
        """创建一个包含特定文件的 ZIP 压缩包."""
        zip_path = tmp_path / "test_archive.zip"

        with zipfile.ZipFile(zip_path, "w") as zf:
            zf.writestr("document.txt", "This is a document")
            zf.writestr("secret_file.docx", "Secret content")
            zf.writestr("data/config.xml", "<config>data</config>")
            zf.writestr("images/photo.jpg", "Image data")

        return zip_path

    @pytest.fixture
    def zip_file_with_exe(self, tmp_path) -> Path:
        """创建包含 exe 文件的 ZIP 压缩包."""
        zip_path = tmp_path / "archive_with_exe.zip"

        with zipfile.ZipFile(zip_path, "w") as zf:
            zf.writestr("readme.txt", "Readme file")
            zf.writestr("setup.exe", "Executable content")
            zf.writestr("installer.msi", "Installer content")

        return zip_path

    @pytest.fixture
    def sample_fileinfo(self, zip_file_with_content) -> FileInfo:
        """创建样本文件信息."""
        return FileInfo(
            file_path=str(zip_file_with_content),
            root_path=str(zip_file_with_content.parent),
            file_type=".zip",
            file_size=zip_file_with_content.stat().st_size,
        )

    def test_init_with_valid_config(self, basic_rule_config: dict) -> None:
        """测试使用有效配置初始化."""
        rule = ZipfileRule(basic_rule_config)
        assert rule.name == "Test Zipfile 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 = ZipfileRule(regex_rule_config)
        assert rule.is_regex is True
        assert rule.compiled_pattern is not None

    def test_match_basic_zip_file(self, basic_rule_config, sample_fileinfo) -> None:
        """测试基础 ZIP 文件匹配."""
        rule = ZipfileRule(basic_rule_config)
        result = rule.match(sample_fileinfo)
        assert result is True

    def test_match_no_match(self, basic_rule_config, tmp_path) -> None:
        """测试不匹配的情况."""
        rule = ZipfileRule(basic_rule_config)

        # 创建不包含匹配内容的 ZIP
        zip_path = tmp_path / "clean_archive.zip"
        with zipfile.ZipFile(zip_path, "w") as zf:
            zf.writestr("clean1.txt", "Clean content")
            zf.writestr("clean2.doc", "Another clean content")

        fileinfo = FileInfo(file_path=str(zip_path), root_path=str(tmp_path), file_type=".zip")

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

    def test_match_unsupported_format(self, basic_rule_config, tmp_path) -> None:
        """测试不支持的压缩格式."""
        rule = ZipfileRule(basic_rule_config)

        # 创建一个 .tar 文件（不支持）
        tar_path = tmp_path / "unsupported.tar"
        tar_path.write_text("fake tar content")

        fileinfo = FileInfo(file_path=str(tar_path), root_path=str(tmp_path), file_type=".tar")

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

    def test_match_regex_pattern(self, regex_rule_config, zip_file_with_exe) -> None:
        """测试正则表达式模式匹配."""
        rule = ZipfileRule(regex_rule_config)
        fileinfo = FileInfo(file_path=str(zip_file_with_exe), root_path=str(zip_file_with_exe.parent), file_type=".zip")

        result = rule.match(fileinfo)
        # 应该匹配到 setup.exe（正则表达式 \.exe$ 会匹配文件名末尾的 .exe）
        assert result is True

    def test_find_all_matches_basic(self, basic_rule_config, sample_fileinfo) -> None:
        """测试查找所有匹配."""
        rule = ZipfileRule(basic_rule_config)
        matches = rule.find_all_matches(sample_fileinfo)

        # 应该匹配到 "secret_file.docx"
        assert len(matches) >= 1
        assert any("secret" in m.match_text.lower() for m in matches)

    def test_find_all_matches_regex(self, regex_rule_config, zip_file_with_exe) -> None:
        """测试查找所有正则匹配."""
        rule = ZipfileRule(regex_rule_config)
        fileinfo = FileInfo(file_path=str(zip_file_with_exe), root_path=str(zip_file_with_exe.parent), file_type=".zip")

        matches = rule.find_all_matches(fileinfo)
        # 应该匹配到 setup.exe
        assert len(matches) >= 1
        # 检查是否有匹配的文本包含 .exe
        assert any(".exe" in m.match_text or m.match_text.endswith(".exe") for m in matches)

    def test_find_all_matches_empty_zip(self, basic_rule_config, tmp_path) -> None:
        """测试空 ZIP 文件."""
        rule = ZipfileRule(basic_rule_config)

        zip_path = tmp_path / "empty.zip"
        with zipfile.ZipFile(zip_path, "w"):
            pass  # 空 ZIP

        fileinfo = FileInfo(file_path=str(zip_path), root_path=str(tmp_path), file_type=".zip")

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

    def test_find_all_matches_corrupted_zip(self, basic_rule_config, tmp_path) -> None:
        """测试损坏的 ZIP 文件."""
        rule = ZipfileRule(basic_rule_config)

        zip_path = tmp_path / "corrupted.zip"
        zip_path.write_text("This is not a valid zip file content")

        fileinfo = FileInfo(file_path=str(zip_path), root_path=str(tmp_path), file_type=".zip")

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

    def test_match_info_structure(self, basic_rule_config, sample_fileinfo) -> None:
        """测试 MatchInfo 对象结构."""
        rule = ZipfileRule(basic_rule_config)
        matches = rule.find_all_matches(sample_fileinfo)

        if matches:
            match = matches[0]
            assert match.file_id == sample_fileinfo.id
            assert match.rule_name == "Test Zipfile Rule"
            assert match.rule_description == "Test description for zipfile"
            assert match.match_type in {"text", "regex"}
            assert match.analysis_type == "zipfile"
            assert match.line_number >= 1
            assert isinstance(match.context_lines, list)
            assert "archive_type" in match.metadata

    def test_metadata_contains_archive_info(self, basic_rule_config, sample_fileinfo) -> None:
        """测试 metadata 包含压缩包信息."""
        rule = ZipfileRule(basic_rule_config)
        matches = rule.find_all_matches(sample_fileinfo)

        if matches:
            import json

            metadata = json.loads(matches[0].metadata)
            assert "archive_type" in metadata
            assert "total_files" in metadata
            assert metadata["archive_type"] == "zip"
            assert metadata["total_files"] > 0

    def test_context_lines_retrieval(self, basic_rule_config, zip_file_with_exe) -> None:
        """测试上下文行检索."""
        rule = ZipfileRule(basic_rule_config)
        fileinfo = FileInfo(file_path=str(zip_file_with_exe), root_path=str(zip_file_with_exe.parent), file_type=".zip")

        matches = rule.find_all_matches(fileinfo)
        if matches:
            match = matches[0]
            assert len(match.context_lines) > 0
            assert len(match.context_lines) <= rule.context_lines * 2 + 1

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

        # 创建包含小写 secret 的 ZIP
        zip_path = tmp_path / "lowercase.zip"
        with zipfile.ZipFile(zip_path, "w") as zf:
            zf.writestr("secret_file.txt", "lowercase secret")

        fileinfo = FileInfo(file_path=str(zip_path), root_path=str(tmp_path), file_type=".zip")

        # 不应该匹配（因为设置了区分大小写）
        result = rule.match(fileinfo)
        assert result is False

    def test_case_insensitive_matching(self, tmp_path: Path) -> None:
        """测试不区分大小写的匹配."""
        config = {
            "name": "Case Insensitive Rule",
            "description": "Case insensitive",
            "pattern": "SECRET",
            "regex": False,
            "case_sensitive": False,
            "context_lines": 3,
        }
        rule = ZipfileRule(config)

        # 创建包含小写 secret 的 ZIP
        zip_path = tmp_path / "lowercase.zip"
        with zipfile.ZipFile(zip_path, "w") as zf:
            zf.writestr("secret_file.txt", "lowercase secret")

        fileinfo = FileInfo(file_path=str(zip_path), root_path=str(tmp_path), file_type=".zip")

        # 应该匹配（因为不区分大小写）
        result = rule.match(fileinfo)
        assert result is True

    def test_read_zip_method_directly(self, tmp_path: Path) -> None:
        """测试直接读取 ZIP 方法."""
        config = {
            "name": "Direct Read Test",
            "description": "Direct read",
            "pattern": "document",
            "regex": False,
            "case_sensitive": False,
            "context_lines": 3,
        }
        rule = ZipfileRule(config)

        zip_path = tmp_path / "test.zip"
        with zipfile.ZipFile(zip_path, "w") as zf:
            zf.writestr("document.txt", "Document content")

        # 直接调用 _read_zip 方法
        result = rule._read_zip(zip_path)
        assert isinstance(result, list)
        assert "document.txt" in result

    def test_invalid_regex_handling(self, tmp_path: Path) -> None:
        """测试无效正则表达式的处理."""
        config = {
            "name": "Invalid Regex",
            "description": "Invalid regex pattern",
            "pattern": "[invalid(regex",
            "regex": True,
            "case_sensitive": False,
            "context_lines": 3,
        }

        # 应该不会抛出异常，而是设置 compiled_pattern 为 None
        rule = ZipfileRule(config)
        assert rule.compiled_pattern is None

        # 创建 ZIP 文件
        zip_path = tmp_path / "test.zip"
        with zipfile.ZipFile(zip_path, "w") as zf:
            zf.writestr("test.txt", "content")

        fileinfo = FileInfo(file_path=str(zip_path), root_path=str(tmp_path), file_type=".zip")

        # 不应该抛出异常
        matches = rule.find_all_matches(fileinfo)
        assert isinstance(matches, list)
