"""
Tests for CLI features and storage backend enhancements.

These tests validate:
- CLI --dry-run option (f1)
- S3 timeout configuration (h2)
"""

import subprocess
import sys
import tempfile
from pathlib import Path
from unittest.mock import patch

import pandas as pd
import pytest


class TestCLIDryRun:
    """Test CLI --dry-run feature (f1)."""

    def setup_method(self):
        """Create temporary input file for testing."""
        self.temp_dir = tempfile.mkdtemp()
        self.input_file = Path(self.temp_dir) / "test_input.parquet"

        # Create a simple test parquet file
        df = pd.DataFrame({"url": ["https://example.com/item1.json", "https://example.com/item2.json"]})
        df.to_parquet(self.input_file)

    def teardown_method(self):
        """Clean up temporary files."""
        import shutil

        shutil.rmtree(self.temp_dir, ignore_errors=True)

    def test_dry_run_exits_with_zero(self):
        """Test that --dry-run exits with code 0."""
        result = subprocess.run(
            [
                sys.executable,
                "-m",
                "earthcatalog.cli",
                "--input",
                str(self.input_file),
                "--output",
                "/tmp/test_output",
                "--scratch",
                "/tmp/test_scratch",
                "--dry-run",
            ],
            capture_output=True,
            text=True,
            cwd=str(Path(__file__).parent.parent.parent),
        )

        assert result.returncode == 0, f"Expected exit code 0, got {result.returncode}. stderr: {result.stderr}"

    def test_dry_run_shows_configuration(self):
        """Test that --dry-run displays configuration info."""
        result = subprocess.run(
            [
                sys.executable,
                "-m",
                "earthcatalog.cli",
                "--input",
                str(self.input_file),
                "--output",
                "/tmp/test_output",
                "--scratch",
                "/tmp/test_scratch",
                "--grid",
                "h3",
                "--grid-resolution",
                "4",
                "--dry-run",
            ],
            capture_output=True,
            text=True,
            cwd=str(Path(__file__).parent.parent.parent),
        )

        # Check that configuration is displayed
        output = result.stdout + result.stderr
        assert "DRY RUN" in output
        assert "h3" in output.lower() or "H3" in output
        assert "validated" in output.lower() or "ready" in output.lower()

    def test_dry_run_does_not_create_output(self):
        """Test that --dry-run doesn't create any output files."""
        output_dir = Path(self.temp_dir) / "output"
        scratch_dir = Path(self.temp_dir) / "scratch"

        subprocess.run(
            [
                sys.executable,
                "-m",
                "earthcatalog.cli",
                "--input",
                str(self.input_file),
                "--output",
                str(output_dir),
                "--scratch",
                str(scratch_dir),
                "--dry-run",
            ],
            capture_output=True,
            text=True,
            cwd=str(Path(__file__).parent.parent.parent),
        )

        # Output directories should not be created
        assert not output_dir.exists()
        assert not scratch_dir.exists()

    def test_dry_run_validates_invalid_config(self):
        """Test that --dry-run still validates configuration."""
        # Test with invalid grid type
        result = subprocess.run(
            [
                sys.executable,
                "-m",
                "earthcatalog.cli",
                "--input",
                str(self.input_file),
                "--output",
                "/tmp/test_output",
                "--scratch",
                "/tmp/test_scratch",
                "--grid",
                "invalid_grid",
                "--dry-run",
            ],
            capture_output=True,
            text=True,
            cwd=str(Path(__file__).parent.parent.parent),
        )

        # Should fail due to invalid grid
        assert result.returncode != 0


class TestS3TimeoutConfiguration:
    """Test S3 storage backend timeout configuration (h2)."""

    @pytest.mark.skipif(not pytest.importorskip("s3fs", reason="s3fs not available"), reason="s3fs library required")
    def test_s3_storage_accepts_timeout_params(self):
        """Test that S3Storage accepts custom timeout parameters."""
        with patch("s3fs.S3FileSystem") as mock_fs:
            from earthcatalog.storage_backends import S3Storage

            S3Storage(
                base_path="s3://test-bucket/prefix/",
                connect_timeout=45.0,
                read_timeout=90.0,
                retries=5,
            )

            # Verify S3FileSystem was called with config
            mock_fs.assert_called_once()
            call_kwargs = mock_fs.call_args[1]
            assert "config_kwargs" in call_kwargs

    @pytest.mark.skipif(not pytest.importorskip("s3fs", reason="s3fs not available"), reason="s3fs library required")
    def test_s3_storage_default_timeouts(self):
        """Test that S3Storage has sensible default timeout values."""
        with patch("s3fs.S3FileSystem") as mock_fs:
            from earthcatalog.storage_backends import S3Storage

            # Create with defaults
            S3Storage(base_path="s3://test-bucket/prefix/")

            # Verify defaults were applied
            mock_fs.assert_called_once()
            call_kwargs = mock_fs.call_args[1]
            assert "config_kwargs" in call_kwargs

            # Extract config from call
            config_kwargs = call_kwargs["config_kwargs"]
            assert "config" in config_kwargs

    def test_storage_backend_direct_instantiation(self):
        """Test direct instantiation of storage backends."""
        from earthcatalog.storage_backends import LocalStorage

        # Test local storage can be instantiated
        local_storage = LocalStorage("/tmp/test")
        assert local_storage is not None
        assert hasattr(local_storage, "exists")
        assert hasattr(local_storage, "open")

    def test_local_storage_interface(self):
        """Test LocalStorage interface for comparison."""
        from earthcatalog.storage_backends import LocalStorage

        storage = LocalStorage("/tmp/test_base")
        assert hasattr(storage, "exists")
        assert hasattr(storage, "open")
        assert hasattr(storage, "makedirs")
        assert hasattr(storage, "remove")
        assert hasattr(storage, "rename")


class TestS3StorageWithMockedBotocore:
    """Test S3 storage with mocked botocore Config."""

    @pytest.mark.skipif(not pytest.importorskip("s3fs", reason="s3fs not available"), reason="s3fs library required")
    def test_botocore_config_applied(self):
        """Test that botocore Config is properly created with timeouts."""
        with patch("s3fs.S3FileSystem"):
            with patch("botocore.config.Config") as mock_config:
                from earthcatalog.storage_backends import S3Storage

                S3Storage(
                    base_path="s3://bucket/",
                    connect_timeout=15.0,
                    read_timeout=30.0,
                    retries=2,
                )

                # Verify Config was called with correct parameters
                mock_config.assert_called_once_with(
                    connect_timeout=15.0,
                    read_timeout=30.0,
                    retries={"max_attempts": 2, "mode": "adaptive"},
                )


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