#!/usr/bin/env python3
"""
Test suite for Issue #135: Instant Markdown base and publication directory

This test suite validates the publication directory functionality for the md-render command,
including directory processing, environment variable handling, and CLI flags.

TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH
State: RED (Tests should fail initially)
"""

import pytest
import tempfile
import os
import shutil
from pathlib import Path
from unittest.mock import patch, MagicMock
import subprocess


class TestPublicationDirectoryManagement:
    """Test publication directory configuration and management."""

    def test_default_publication_directory_is_notes_home(self):
        """Test that default publication directory is ~/Notes/."""
        from markitect.plugins.builtin.markdown_commands import get_publication_directory

        with patch.dict(os.environ, {}, clear=True):
            # Remove any existing MARKITECT_PUBLICATION_DIR env var
            if 'MARKITECT_PUBLICATION_DIR' in os.environ:
                del os.environ['MARKITECT_PUBLICATION_DIR']

            pub_dir = get_publication_directory()
            expected = Path.home() / "Notes"
            assert pub_dir == expected

    def test_environment_variable_overrides_default_publication_directory(self):
        """Test that MARKITECT_PUBLICATION_DIR environment variable overrides default."""
        from markitect.plugins.builtin.markdown_commands import get_publication_directory

        custom_dir = "/tmp/custom_publication"
        with patch.dict(os.environ, {'MARKITECT_PUBLICATION_DIR': custom_dir}):
            pub_dir = get_publication_directory()
            assert pub_dir == Path(custom_dir)

    def test_publication_directory_creation_when_nonexistent(self):
        """Test that publication directory is created when it doesn't exist."""
        from markitect.plugins.builtin.markdown_commands import ensure_publication_directory

        with tempfile.TemporaryDirectory() as tmpdir:
            pub_dir = Path(tmpdir) / "nonexistent" / "publication"
            assert not pub_dir.exists()

            ensure_publication_directory(pub_dir)
            assert pub_dir.exists()
            assert pub_dir.is_dir()

    def test_publication_directory_path_normalization(self):
        """Test that publication directory paths are properly normalized."""
        from markitect.plugins.builtin.markdown_commands import normalize_publication_path

        # Test tilde expansion
        path_with_tilde = "~/Documents/Notes"
        normalized = normalize_publication_path(path_with_tilde)
        assert str(normalized).startswith(str(Path.home()))

        # Test relative path resolution
        relative_path = "./publication"
        normalized = normalize_publication_path(relative_path)
        assert normalized.is_absolute()


class TestSingleFileProcessing:
    """Test single markdown file processing behavior."""

    def setup_method(self):
        """Set up test environment with temporary files."""
        self.temp_dir = tempfile.mkdtemp()
        self.test_md_file = Path(self.temp_dir) / "test.md"
        self.test_md_file.write_text("# Test Document\n\nThis is a test.")

        # Set up publication directory
        self.pub_dir = Path(self.temp_dir) / "publication"
        self.pub_dir.mkdir(exist_ok=True)

    def teardown_method(self):
        """Clean up test environment."""
        shutil.rmtree(self.temp_dir)

    def test_single_file_default_html_next_to_md(self):
        """Test that by default, single file HTML is generated next to MD file."""
        from markitect.plugins.builtin.markdown_commands import process_single_file

        result = process_single_file(
            input_file=self.test_md_file,
            use_publication_dir=False,
            publication_dir=self.pub_dir
        )

        expected_output = self.test_md_file.with_suffix('.html')
        assert result == expected_output
        # This will fail until implementation is added
        assert expected_output.exists()

    def test_single_file_with_use_publication_dir_flag(self):
        """Test single file with --use-publication-dir places HTML in publication directory."""
        from markitect.plugins.builtin.markdown_commands import process_single_file

        result = process_single_file(
            input_file=self.test_md_file,
            use_publication_dir=True,
            publication_dir=self.pub_dir
        )

        expected_output = self.pub_dir / "test.html"
        assert result == expected_output
        # This will fail until implementation is added
        assert expected_output.exists()

    def test_single_file_output_naming_conventions(self):
        """Test that output file naming follows conventions."""
        from markitect.plugins.builtin.markdown_commands import get_output_filename

        # Test basic naming
        input_file = Path("document.md")
        output_name = get_output_filename(input_file)
        assert output_name == "document.html"

        # Test with complex filename
        input_file = Path("my-document_v2.md")
        output_name = get_output_filename(input_file)
        assert output_name == "my-document_v2.html"


class TestDirectoryProcessing:
    """Test directory tree processing functionality."""

    def setup_method(self):
        """Set up test directory structure."""
        self.temp_dir = tempfile.mkdtemp()
        self.base_dir = Path(self.temp_dir) / "source"
        self.pub_dir = Path(self.temp_dir) / "publication"

        # Create test directory structure
        self.base_dir.mkdir()
        self.pub_dir.mkdir()

        # Create test markdown files
        (self.base_dir / "root.md").write_text("# Root Document")
        (self.base_dir / "subdir").mkdir()
        (self.base_dir / "subdir" / "sub.md").write_text("# Sub Document")
        (self.base_dir / "subdir" / "deep").mkdir()
        (self.base_dir / "subdir" / "deep" / "deep.md").write_text("# Deep Document")

        # Create non-markdown files (should be ignored)
        (self.base_dir / "readme.txt").write_text("Not markdown")
        (self.base_dir / "image.png").write_bytes(b"fake image data")

    def teardown_method(self):
        """Clean up test environment."""
        shutil.rmtree(self.temp_dir)

    def test_directory_default_html_in_publication_dir_with_structure(self):
        """Test that directory processing puts HTML in publication directory with preserved structure."""
        from markitect.plugins.builtin.markdown_commands import process_directory

        results = process_directory(
            input_dir=self.base_dir,
            use_publication_dir=True,
            publication_dir=self.pub_dir
        )

        # Check that all expected HTML files are in publication directory
        expected_files = [
            self.pub_dir / "root.html",
            self.pub_dir / "subdir" / "sub.html",
            self.pub_dir / "subdir" / "deep" / "deep.html"
        ]

        assert len(results) == 3
        for expected_file in expected_files:
            assert expected_file in results
            # This will fail until implementation is added
            assert expected_file.exists()

    def test_directory_with_dont_use_publication_dir_html_next_to_md(self):
        """Test directory processing with --dont-use-publication-dir places HTML next to MD files."""
        from markitect.plugins.builtin.markdown_commands import process_directory

        results = process_directory(
            input_dir=self.base_dir,
            use_publication_dir=False,
            publication_dir=self.pub_dir
        )

        # Check that HTML files are next to their corresponding MD files
        expected_files = [
            self.base_dir / "root.html",
            self.base_dir / "subdir" / "sub.html",
            self.base_dir / "subdir" / "deep" / "deep.html"
        ]

        assert len(results) == 3
        for expected_file in expected_files:
            assert expected_file in results
            # This will fail until implementation is added
            assert expected_file.exists()

    def test_directory_recursive_traversal(self):
        """Test that directory processing recursively finds all markdown files."""
        from markitect.plugins.builtin.markdown_commands import find_markdown_files

        md_files = find_markdown_files(self.base_dir)

        expected_files = [
            self.base_dir / "root.md",
            self.base_dir / "subdir" / "sub.md",
            self.base_dir / "subdir" / "deep" / "deep.md"
        ]

        assert len(md_files) == 3
        for expected_file in expected_files:
            assert expected_file in md_files

    def test_directory_structure_preservation(self):
        """Test that directory structure is preserved in publication directory."""
        from markitect.plugins.builtin.markdown_commands import get_relative_output_path

        # Test relative path calculation
        source_file = self.base_dir / "subdir" / "deep" / "deep.md"
        relative_path = get_relative_output_path(source_file, self.base_dir, self.pub_dir)

        expected_path = self.pub_dir / "subdir" / "deep" / "deep.html"
        assert relative_path == expected_path


class TestCLIIntegration:
    """Test CLI integration with new flags."""

    def setup_method(self):
        """Set up test environment."""
        self.temp_dir = tempfile.mkdtemp()
        self.test_file = Path(self.temp_dir) / "test.md"
        self.test_file.write_text("# Test Document")

    def teardown_method(self):
        """Clean up test environment."""
        shutil.rmtree(self.temp_dir)

    def test_md_render_command_has_use_publication_dir_flag(self):
        """Test that md-render command has --use-publication-dir flag."""
        result = subprocess.run(
            ["markitect", "md-render", "--help"],
            capture_output=True,
            text=True
        )

        assert "--use-publication-dir" in result.stdout
        assert "publication directory" in result.stdout.lower()

    def test_md_render_command_has_dont_use_publication_dir_flag(self):
        """Test that md-render command has --dont-use-publication-dir flag."""
        result = subprocess.run(
            ["markitect", "md-render", "--help"],
            capture_output=True,
            text=True
        )

        assert "--dont-use-publication-dir" in result.stdout

    def test_md_render_with_directory_input(self):
        """Test that md-render command accepts directory as input."""
        # Create a test directory with markdown files
        test_dir = Path(self.temp_dir) / "test_dir"
        test_dir.mkdir()
        (test_dir / "test.md").write_text("# Test")

        # This should not fail with directory input
        result = subprocess.run(
            ["markitect", "md-render", str(test_dir)],
            capture_output=True,
            text=True,
            timeout=30
        )

        # Should not error on directory input
        assert result.returncode == 0 or "directory" in result.stderr

    def test_environment_variable_integration(self):
        """Test that MARKITECT_PUBLICATION_DIR environment variable is respected."""
        pub_dir = Path(self.temp_dir) / "custom_pub"
        pub_dir.mkdir()

        env = os.environ.copy()
        env['MARKITECT_PUBLICATION_DIR'] = str(pub_dir)

        result = subprocess.run(
            ["markitect", "md-render", str(self.test_file), "--use-publication-dir"],
            capture_output=True,
            text=True,
            env=env,
            timeout=30
        )

        # Should succeed or at least recognize the flag
        # This will fail until implementation is complete
        assert result.returncode == 0


class TestEdgeCases:
    """Test edge cases and error conditions."""

    def setup_method(self):
        """Set up test environment."""
        self.temp_dir = tempfile.mkdtemp()

    def teardown_method(self):
        """Clean up test environment."""
        shutil.rmtree(self.temp_dir)

    def test_empty_directory_processing(self):
        """Test processing of empty directory."""
        from markitect.plugins.builtin.markdown_commands import process_directory

        empty_dir = Path(self.temp_dir) / "empty"
        empty_dir.mkdir()
        pub_dir = Path(self.temp_dir) / "pub"
        pub_dir.mkdir()

        results = process_directory(
            input_dir=empty_dir,
            use_publication_dir=True,
            publication_dir=pub_dir
        )

        assert results == []

    def test_mixed_content_directory_processing(self):
        """Test directory with mixed content (markdown and other files)."""
        from markitect.plugins.builtin.markdown_commands import find_markdown_files

        mixed_dir = Path(self.temp_dir) / "mixed"
        mixed_dir.mkdir()

        # Create various file types
        (mixed_dir / "document.md").write_text("# Document")
        (mixed_dir / "readme.txt").write_text("Text file")
        (mixed_dir / "image.jpg").write_bytes(b"fake image")
        (mixed_dir / "script.py").write_text("print('hello')")
        (mixed_dir / "another.md").write_text("# Another")

        md_files = find_markdown_files(mixed_dir)

        # Should only find markdown files
        assert len(md_files) == 2
        assert all(f.suffix == '.md' for f in md_files)

    def test_nonexistent_input_error_handling(self):
        """Test error handling for nonexistent input files/directories."""
        from markitect.plugins.builtin.markdown_commands import process_single_file

        nonexistent_file = Path(self.temp_dir) / "nonexistent.md"
        pub_dir = Path(self.temp_dir) / "pub"

        with pytest.raises(FileNotFoundError):
            process_single_file(
                input_file=nonexistent_file,
                use_publication_dir=False,
                publication_dir=pub_dir
            )


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