"""
Integration test for EarthCatalog async HTTP with mocked STAC catalog endpoints.

This test validates that the async HTTP implementation works correctly with
simulated STAC catalog services, ensuring compatibility with real-world data patterns.
Optimized for fast execution while maintaining comprehensive coverage.
"""

# mypy: ignore-errors

import logging
import tempfile
import time
from pathlib import Path

import pandas as pd
import pytest

# Import async HTTP testing utilities
try:
    import aiohttp
    from aioresponses import aioresponses

    HAS_ASYNC_TEST_SUPPORT = True
except ImportError:
    HAS_ASYNC_TEST_SUPPORT = False
    aioresponses = None  # type: ignore
    aiohttp = None  # type: ignore

# Configure test logging
logging.basicConfig(level=logging.INFO)


@pytest.fixture
def mock_stac_urls() -> list[str]:
    """Provide a set of STAC catalog URLs for integration testing."""
    return [
        # Microsoft Planetary Computer - Sentinel-2 L2A
        "https://planetarycomputer.microsoft.com/api/stac/v1/collections/sentinel-2-l2a/items?limit=5&bbox=-122.5,37.7,-122.3,37.9",
        # AWS Earth Search - Landsat Collection 2 L2
        "https://earth-search.aws.element84.com/v1/collections/landsat-c2l2-sr/items?limit=5&bbox=-122.5,37.7,-122.3,37.9",
        # ESA Copernicus Data Space - Sentinel-1
        "https://catalogue.dataspace.copernicus.eu/stac/collections/SENTINEL-1/items?limit=5&bbox=-122.5,37.7,-122.3,37.9",
    ]


@pytest.fixture
def temp_test_dir():
    """Create temporary directory for test files."""
    with tempfile.TemporaryDirectory() as temp_dir:
        yield Path(temp_dir)


@pytest.fixture
def sample_stac_response():
    """Sample STAC FeatureCollection response for mocking."""
    return {
        "type": "FeatureCollection",
        "links": [],
        "features": [
            {
                "id": "S2A_MSIL2A_20241201T123456_N0500_R001_T10SDF_20241201T154321",
                "type": "Feature",
                "geometry": {"type": "Point", "coordinates": [-122.4, 37.8]},
                "properties": {"datetime": "2024-12-01T12:34:56Z", "collection": "sentinel-2-l2a"},
            },
            {
                "id": "LC08_L2SP_123456_20241201_20241201_02_T1",
                "type": "Feature",
                "geometry": {"type": "Point", "coordinates": [-122.35, 37.85]},
                "properties": {"datetime": "2024-12-01T12:34:56Z", "collection": "landsat-c2l2-sr"},
            },
        ],
    }


def create_test_input_file(urls: list[str], output_path: Path) -> None:
    """Create a parquet file with STAC URLs for testing."""
    df = pd.DataFrame(
        {
            "url": urls,
            "collection": [f"test_collection_{i}" for i in range(len(urls))],
            "grid_id": [f"h{i:02d}v{i:02d}" for i in range(len(urls))],
        }
    )
    df.to_parquet(output_path, index=False)


@pytest.mark.skipif(not HAS_ASYNC_TEST_SUPPORT, reason="aioresponses not available")
@pytest.mark.asyncio
async def test_async_http_client_with_mock_stac(sample_stac_response):
    """Test AsyncHTTPClient with mocked STAC catalog endpoints."""
    pytest.importorskip("aiohttp", reason="aiohttp required for async HTTP client")

    from earthcatalog.async_http_client import AsyncHTTPClient

    # Test URLs (mocked for fast, reliable testing)
    test_urls = [
        "https://planetarycomputer.microsoft.com/api/stac/v1/collections/sentinel-2-l2a/items?limit=2&bbox=-122.5,37.7,-122.3,37.9",
        "https://earth-search.aws.element84.com/v1/collections/landsat-c2l2-sr/items?limit=2&bbox=-122.5,37.7,-122.3,37.9",
    ]

    with aioresponses() as mock_responses:
        # Mock all URLs with successful STAC responses
        for url in test_urls:
            mock_responses.get(url, payload=sample_stac_response)

        async with AsyncHTTPClient(concurrent_requests=2, request_timeout=5) as client:
            # Use download_batch method which returns RequestResult objects
            results = await client.download_batch(test_urls)

            # Validate results
            assert len(results) == len(test_urls), "Should receive result for each URL"

            successful_results = [r for r in results if r.success and r.data]
            assert len(successful_results) == len(test_urls), "All requests should succeed with mocked responses"

            for i, result in enumerate(successful_results):
                assert result.data is not None, f"Result {i} should have data"

                # Validate STAC response structure (data is already parsed)
                data = result.data
                assert "type" in data, "STAC response should have 'type' field"
                assert "features" in data, "STAC response should have 'features' field"
                assert isinstance(data["features"], list), "Features should be a list"
                assert len(data["features"]) == 2, "Should have 2 STAC items as mocked"

                logging.info(f"URL {i}: Retrieved {len(data['features'])} STAC items")


@pytest.mark.skipif(not HAS_ASYNC_TEST_SUPPORT, reason="aioresponses not available")
@pytest.mark.asyncio
async def test_batch_downloader_with_mock_stac(sample_stac_response):
    """Test BatchDownloader with mocked STAC catalog endpoints."""
    pytest.importorskip("aiohttp", reason="aiohttp required for async HTTP client")

    from earthcatalog.async_http_client import BatchDownloader

    # Use mocked endpoints for fast testing
    test_urls = [
        "https://planetarycomputer.microsoft.com/api/stac/v1/collections/sentinel-2-l2a/items?limit=1&bbox=-122.4,37.8,-122.35,37.85",
    ]

    with aioresponses() as mock_responses:
        # Mock with STAC response
        mock_responses.get(test_urls[0], payload=sample_stac_response)

        downloader = BatchDownloader(concurrent_requests=1, batch_size=1, request_timeout=5, retry_attempts=2)

        try:
            results = await downloader.download_all(test_urls)

            # Validate results (download_all returns list of responses, not individual items)
            assert len(results) == 1, "Should receive 1 response from mocked endpoint"

            # The response is a FeatureCollection
            response = results[0]
            assert response is not None, "Response should not be None"
            assert isinstance(response, dict), "Response should be dict"

            # Validate STAC FeatureCollection structure
            assert "type" in response, "Should have STAC 'type' field"
            assert response["type"] == "FeatureCollection", "Should be a FeatureCollection"
            assert "features" in response, "Should have 'features' field"
            assert len(response["features"]) == 2, "Should have 2 features in the collection"

            logging.info(f"Batch download: Retrieved FeatureCollection with {len(response['features'])} features")

        finally:
            await downloader.close()


def test_processing_config_async_defaults():
    """Test that ProcessingConfig has correct async defaults."""
    from earthcatalog.ingestion_pipeline import ProcessingConfig

    config = ProcessingConfig(input_file="test.parquet", output_catalog="./output", scratch_location="./scratch")

    # Validate async HTTP is enabled by default
    assert config.enable_concurrent_http is True, "Async HTTP should be enabled by default"
    assert config.concurrent_requests == 50, "Default concurrent requests should be 50"
    assert config.batch_size == 1000, "Default batch size should be 1000"
    assert config.request_timeout == 30, "Default timeout should be 30 seconds"
    assert config.retry_attempts == 3, "Default retry attempts should be 3"


@pytest.mark.asyncio
async def test_end_to_end_async_integration(mock_stac_urls, temp_test_dir):
    """End-to-end integration test with mocked STAC URLs."""
    pytest.importorskip("aiohttp", reason="aiohttp required for async processing")

    from earthcatalog.ingestion_pipeline import LocalProcessor, ProcessingConfig, STACIngestionPipeline

    # Setup test files
    input_file = temp_test_dir / "test_stac_urls.parquet"
    output_catalog = temp_test_dir / "output_catalog"
    scratch_location = temp_test_dir / "scratch"

    # Use subset of URLs for faster testing
    test_urls = mock_stac_urls[:2]  # Only test first 2 URLs
    create_test_input_file(test_urls, input_file)

    # Configure for async processing
    config = ProcessingConfig(
        input_file=str(input_file),
        output_catalog=str(output_catalog),
        scratch_location=str(scratch_location),
        enable_concurrent_http=True,
        concurrent_requests=2,  # Conservative for testing
        batch_size=10,
        request_timeout=5,  # Short timeout for fast testing
        retry_attempts=1,  # Minimal retries for speed
        grid_system="h3",
        grid_resolution=2,
        temporal_bin="year",
    )

    # Create processor and pipeline
    processor = LocalProcessor(n_workers=1)  # Single worker for testing

    try:
        pipeline = STACIngestionPipeline(config, processor)

        # Just test that the pipeline initializes correctly with async config
        # Running the full pipeline with STAC catalog URLs is complex for unit testing
        # since they return FeatureCollections, not individual STAC items
        assert pipeline.config.enable_concurrent_http is True
        assert pipeline.config.concurrent_requests == 2
        assert pipeline.config.request_timeout == 5

        # Test that the async HTTP client is detected
        from earthcatalog.async_http_client import HAS_ASYNC_HTTP

        if HAS_ASYNC_HTTP:
            logging.info("End-to-end async integration: Pipeline configured correctly with async HTTP")
        else:
            logging.info("End-to-end async integration: Pipeline configured for sync fallback")

    except (ValueError, TypeError, AttributeError, ImportError, RuntimeError) as e:
        pytest.skip(f"Pipeline initialization failed: {e}")
    finally:
        processor.close()


@pytest.mark.parametrize("concurrent_requests", [1, 5, 10])
@pytest.mark.skipif(not HAS_ASYNC_TEST_SUPPORT, reason="aioresponses not available")
@pytest.mark.asyncio
async def test_concurrent_requests_scaling(concurrent_requests, sample_stac_response):
    """Test async performance scales with concurrent requests using mocked responses."""
    pytest.importorskip("aiohttp", reason="aiohttp required for async HTTP client")

    from earthcatalog.async_http_client import AsyncHTTPClient

    # Use mocked responses for fast, reliable testing
    test_urls = [f"https://example.com/stac/item_{i}.json" for i in range(5)]

    start_time = time.time()

    with aioresponses() as mock_responses:
        # Mock all URLs with successful responses
        for url in test_urls:
            mock_responses.get(url, payload=sample_stac_response)

        async with AsyncHTTPClient(concurrent_requests=concurrent_requests, request_timeout=5) as client:
            results = await client.download_batch(test_urls)

    total_time = time.time() - start_time
    success_count = sum(1 for r in results if r.success)

    # Validate performance characteristics
    assert success_count == len(test_urls), "All mocked requests should succeed"

    # With mocked responses, this should be very fast (under 1 second)
    assert total_time < 2.0, f"Mocked requests should be fast: {total_time:.1f}s"

    logging.info(
        f"Concurrent requests {concurrent_requests}: {success_count}/{len(test_urls)} success in {total_time:.2f}s"
    )


if __name__ == "__main__":
    # Run integration tests directly
    pytest.main([__file__, "-v", "-s"])
