"""
Test suite for Issue #148 - Core Infrastructure for Explode-Implode Variants

Tests the foundational infrastructure components that support multiple
explode-implode variants with manifest-based reversibility.
"""

import pytest
import tempfile
import yaml
from pathlib import Path
from datetime import datetime

from markitect.explode_variants import (
    ExplodeVariant, ExplodeMode, ManifestVersion, DetectionConfidence,
    BaseVariant, ExplodeOptions, ImplodeOptions, ExplodeResult, ImplodeResult,
    ManifestManager, ManifestData, StructureEntry,
    VariantDetector, DetectionResult
)


class TestExplodeVariantEnum:
    """Test the ExplodeVariant enum and related enums."""

    def test_explode_variant_values(self):
        """Test that all expected variants are available."""
        assert ExplodeVariant.FLAT.value == "flat"
        assert ExplodeVariant.HIERARCHICAL.value == "hierarchical"
        assert ExplodeVariant.SEMANTIC.value == "semantic"

    def test_explode_mode_values(self):
        """Test ExplodeMode enum values."""
        assert ExplodeMode.STANDARD.value == "standard"
        assert ExplodeMode.LEGACY.value == "legacy"
        assert ExplodeMode.PREVIEW.value == "preview"

    def test_detection_confidence_values(self):
        """Test DetectionConfidence enum values."""
        assert DetectionConfidence.HIGH.value == "high"
        assert DetectionConfidence.MEDIUM.value == "medium"
        assert DetectionConfidence.LOW.value == "low"
        assert DetectionConfidence.UNKNOWN.value == "unknown"


class TestStructureEntry:
    """Test the StructureEntry dataclass."""

    def test_structure_entry_creation(self):
        """Test creating a StructureEntry."""
        entry = StructureEntry(
            type="h1",
            title="Chapter 1",
            path="chapter_1/index.md",
            order=1,
            parent=None,
            level=1,
            original_line=5
        )

        assert entry.type == "h1"
        assert entry.title == "Chapter 1"
        assert entry.path == "chapter_1/index.md"
        assert entry.order == 1
        assert entry.parent is None
        assert entry.level == 1
        assert entry.original_line == 5

    def test_structure_entry_defaults(self):
        """Test StructureEntry with default values."""
        entry = StructureEntry(
            type="h2",
            title="Section",
            path="section.md",
            order=2
        )

        assert entry.parent is None
        assert entry.level == 1
        assert entry.original_line is None


class TestManifestData:
    """Test the ManifestData dataclass."""

    def test_manifest_data_creation(self):
        """Test creating ManifestData."""
        manifest = ManifestData(
            explosion_type="flat",
            original_file="book.md",
            created="2025-10-12T19:30:00Z",
            markitect_version="0.1.0"
        )

        assert manifest.explosion_type == "flat"
        assert manifest.original_file == "book.md"
        assert manifest.created == "2025-10-12T19:30:00Z"
        assert manifest.markitect_version == "0.1.0"
        assert manifest.manifest_version == ManifestVersion.V1_0.value


class TestManifestManager:
    """Test the ManifestManager class."""

    def test_manifest_manager_initialization(self):
        """Test ManifestManager initialization."""
        manager = ManifestManager("0.1.0")
        assert manager.markitect_version == "0.1.0"
        assert manager.MANIFEST_FILENAME == "manifest.md"

    def test_create_manifest(self):
        """Test creating a manifest file."""
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)
            manager = ManifestManager("0.1.0")

            # Create test structure
            structure = [
                StructureEntry(
                    type="h1",
                    title="Book Title",
                    path="book_title/index.md",
                    order=1
                ),
                StructureEntry(
                    type="h2",
                    title="Chapter 1",
                    path="book_title/chapter_1.md",
                    order=2,
                    parent="Book Title"
                )
            ]

            manifest_path = manager.create_manifest(
                output_dir=temp_path,
                original_file=Path("book.md"),
                variant=ExplodeVariant.FLAT,
                structure=structure,
                preservation_options={
                    "front_matter": True,
                    "section_order": True,
                    "heading_levels": True
                }
            )

            assert manifest_path.exists()
            assert manifest_path.name == "manifest.md"

            # Verify content
            content = manifest_path.read_text(encoding='utf-8')
            assert "explosion_type: flat" in content
            assert "original_file: book.md" in content
            assert "Book Title" in content
            assert "Chapter 1" in content

    def test_read_manifest(self):
        """Test reading a manifest file."""
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)
            manager = ManifestManager("0.1.0")

            # Create manifest
            structure = [
                StructureEntry(
                    type="h1",
                    title="Test Title",
                    path="test_title/index.md",
                    order=1
                )
            ]

            manifest_path = manager.create_manifest(
                output_dir=temp_path,
                original_file=Path("test.md"),
                variant=ExplodeVariant.HIERARCHICAL,
                structure=structure
            )

            # Read manifest back
            manifest_data = manager.read_manifest(temp_path)

            assert manifest_data is not None
            assert manifest_data.explosion_type == "hierarchical"
            assert manifest_data.original_file == "test.md"
            assert manifest_data.markitect_version == "0.1.0"
            assert len(manifest_data.structure) == 1
            assert manifest_data.structure[0].title == "Test Title"

    def test_read_nonexistent_manifest(self):
        """Test reading manifest from directory without one."""
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)
            manager = ManifestManager("0.1.0")

            manifest_data = manager.read_manifest(temp_path)
            assert manifest_data is None

    def test_validate_manifest(self):
        """Test manifest validation."""
        manager = ManifestManager("0.1.0")

        # Valid manifest
        valid_manifest = ManifestData(
            explosion_type="flat",
            original_file="test.md",
            created="2025-10-12T19:30:00Z",
            markitect_version="0.1.0"
        )

        errors = manager.validate_manifest(valid_manifest)
        assert len(errors) == 0

        # Invalid manifest
        invalid_manifest = ManifestData(
            explosion_type="invalid_variant",
            original_file="",
            created="",
            markitect_version="0.1.0"
        )

        errors = manager.validate_manifest(invalid_manifest)
        assert len(errors) > 0
        assert any("Invalid explosion_type" in error for error in errors)
        assert any("Missing original_file" in error for error in errors)


class TestVariantDetector:
    """Test the VariantDetector class."""

    def test_variant_detector_initialization(self):
        """Test VariantDetector initialization."""
        detector = VariantDetector()
        assert detector.manifest_manager is not None

    def test_detect_variant_nonexistent_directory(self):
        """Test variant detection on nonexistent directory."""
        detector = VariantDetector()
        result = detector.detect_variant(Path("/nonexistent/path"))

        assert result.variant is None
        assert result.confidence == DetectionConfidence.UNKNOWN
        assert result.score == 0.0
        assert not result.manifest_found
        assert "does not exist" in result.evidence[0]

    def test_detect_variant_with_manifest(self):
        """Test variant detection when manifest is present."""
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)

            # Create a manifest
            manager = ManifestManager("0.1.0")
            manager.create_manifest(
                output_dir=temp_path,
                original_file=Path("test.md"),
                variant=ExplodeVariant.HIERARCHICAL,
                structure=[]
            )

            detector = VariantDetector()
            result = detector.detect_variant(temp_path)

            assert result.variant == ExplodeVariant.HIERARCHICAL
            assert result.confidence == DetectionConfidence.HIGH
            assert result.score == 1.0
            assert result.manifest_found
            assert result.manifest_data is not None

    def test_detect_variant_hierarchical_pattern(self):
        """Test variant detection based on hierarchical naming patterns."""
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)

            # Create directories with numbered prefixes
            (temp_path / "01_chapter_one").mkdir()
            (temp_path / "02_chapter_two").mkdir()
            (temp_path / "03_chapter_three").mkdir()

            detector = VariantDetector()
            result = detector.detect_variant(temp_path)

            assert result.variant in [ExplodeVariant.HIERARCHICAL, ExplodeVariant.FLAT]
            assert result.confidence in [DetectionConfidence.HIGH, DetectionConfidence.MEDIUM]
            assert not result.manifest_found

    def test_detect_variant_semantic_pattern(self):
        """Test variant detection based on semantic directory names."""
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)

            # Create semantic directories
            (temp_path / "parts").mkdir()
            (temp_path / "chapters").mkdir()
            (temp_path / "appendices").mkdir()

            detector = VariantDetector()
            result = detector.detect_variant(temp_path)

            # Should detect semantic or fall back to flat
            assert result.variant in [ExplodeVariant.SEMANTIC, ExplodeVariant.FLAT]
            assert not result.manifest_found

    def test_is_exploded_directory(self):
        """Test detection of exploded directory structures."""
        detector = VariantDetector()

        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)

            # Empty directory should not be detected as exploded
            assert not detector.is_exploded_directory(temp_path)

            # Directory with manifest should be detected
            (temp_path / "manifest.md").write_text("test manifest")
            assert detector.is_exploded_directory(temp_path)

            # Clean up and test other patterns
            (temp_path / "manifest.md").unlink()

            # Directory with numbered subdirs and markdown should be detected
            subdir = temp_path / "01_chapter"
            subdir.mkdir()
            (subdir / "index.md").write_text("test content")
            assert detector.is_exploded_directory(temp_path)


class TestExplodeImplodeOptions:
    """Test the options dataclasses."""

    def test_explode_options_defaults(self):
        """Test ExplodeOptions with defaults."""
        options = ExplodeOptions(variant=ExplodeVariant.FLAT)

        assert options.variant == ExplodeVariant.FLAT
        assert options.mode == ExplodeMode.STANDARD
        assert options.output_dir is None
        assert options.max_depth is None
        assert options.preserve_front_matter is True
        assert options.section_spacing == 2
        assert options.dry_run is False
        assert options.verbose is False
        assert options.create_manifest is True

    def test_implode_options_defaults(self):
        """Test ImplodeOptions with defaults."""
        options = ImplodeOptions()

        assert options.output_file is None
        assert options.force_variant is None
        assert options.preserve_front_matter is True
        assert options.section_spacing == 2
        assert options.dry_run is False
        assert options.verbose is False
        assert options.overwrite is False


class TestResults:
    """Test the result dataclasses."""

    def test_explode_result_creation(self):
        """Test creating an ExplodeResult."""
        result = ExplodeResult(
            success=True,
            output_directory=Path("/test/output"),
            files_created=[Path("file1.md"), Path("file2.md")],
            manifest_path=Path("/test/output/manifest.md"),
            warnings=["Warning 1"],
            errors=[],
            variant_used=ExplodeVariant.FLAT
        )

        assert result.success is True
        assert result.output_directory == Path("/test/output")
        assert len(result.files_created) == 2
        assert result.manifest_path == Path("/test/output/manifest.md")
        assert len(result.warnings) == 1
        assert len(result.errors) == 0
        assert result.variant_used == ExplodeVariant.FLAT

    def test_implode_result_creation(self):
        """Test creating an ImplodeResult."""
        result = ImplodeResult(
            success=True,
            output_file=Path("/test/output.md"),
            files_processed=[Path("file1.md"), Path("file2.md")],
            variant_detected=ExplodeVariant.HIERARCHICAL,
            warnings=[],
            errors=[]
        )

        assert result.success is True
        assert result.output_file == Path("/test/output.md")
        assert len(result.files_processed) == 2
        assert result.variant_detected == ExplodeVariant.HIERARCHICAL
        assert len(result.warnings) == 0
        assert len(result.errors) == 0


if __name__ == '__main__':
    pytest.main([__file__, "-v"])