"""Tests for file I/O tools."""

import json
import os
import tempfile
from pathlib import Path

import pytest

from gis_mcp.tools.files import (
    read_geo_file,
    write_geo_file,
    _get_driver_for_path,
)


class TestGetDriverForPath:
    """Tests for driver detection."""

    def test_geojson_extension(self):
        """Test GeoJSON extension detection."""
        assert _get_driver_for_path("file.geojson") == "GeoJSON"
        assert _get_driver_for_path("file.json") == "GeoJSON"
        assert _get_driver_for_path("FILE.GEOJSON") == "GeoJSON"

    def test_shapefile_extension(self):
        """Test Shapefile extension detection."""
        assert _get_driver_for_path("file.shp") == "ESRI Shapefile"
        assert _get_driver_for_path("FILE.SHP") == "ESRI Shapefile"

    def test_geopackage_extension(self):
        """Test GeoPackage extension detection."""
        assert _get_driver_for_path("file.gpkg") == "GPKG"

    def test_unsupported_extension(self):
        """Test unsupported extension returns None."""
        assert _get_driver_for_path("file.txt") is None
        assert _get_driver_for_path("file.csv") is None
        assert _get_driver_for_path("file") is None


class TestReadGeoFile:
    """Tests for reading geospatial files."""

    @pytest.mark.asyncio
    async def test_file_not_found(self):
        """Test reading non-existent file."""
        result = await read_geo_file("/nonexistent/path/file.geojson")
        assert result["success"] is False
        assert "not found" in result["error"].lower()

    @pytest.mark.asyncio
    async def test_unsupported_format(self):
        """Test reading unsupported format."""
        with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as f:
            f.write(b"not a geo file")
            temp_path = f.name

        try:
            result = await read_geo_file(temp_path)
            assert result["success"] is False
            assert "unsupported" in result["error"].lower()
        finally:
            os.unlink(temp_path)

    @pytest.mark.asyncio
    async def test_read_valid_geojson(self):
        """Test reading a valid GeoJSON file."""
        geojson_content = {
            "type": "FeatureCollection",
            "features": [
                {
                    "type": "Feature",
                    "geometry": {
                        "type": "Point",
                        "coordinates": [2.3522, 48.8566]
                    },
                    "properties": {
                        "name": "Paris",
                        "population": 2161000
                    }
                },
                {
                    "type": "Feature",
                    "geometry": {
                        "type": "Point",
                        "coordinates": [-0.1278, 51.5074]
                    },
                    "properties": {
                        "name": "London",
                        "population": 8982000
                    }
                }
            ]
        }

        with tempfile.NamedTemporaryFile(suffix=".geojson", delete=False, mode="w") as f:
            json.dump(geojson_content, f)
            temp_path = f.name

        try:
            result = await read_geo_file(temp_path)
            assert result["success"] is True
            assert result["data"]["type"] == "FeatureCollection"
            assert result["data"]["feature_count"] == 2
            assert len(result["data"]["features"]) == 2
            assert result["metadata"]["driver"] == "GeoJSON"
            assert "geometry_types" in result["metadata"]
        finally:
            os.unlink(temp_path)

    @pytest.mark.asyncio
    async def test_read_with_limit(self):
        """Test reading with feature limit."""
        geojson_content = {
            "type": "FeatureCollection",
            "features": [
                {
                    "type": "Feature",
                    "geometry": {"type": "Point", "coordinates": [i, i]},
                    "properties": {"id": i}
                }
                for i in range(10)
            ]
        }

        with tempfile.NamedTemporaryFile(suffix=".geojson", delete=False, mode="w") as f:
            json.dump(geojson_content, f)
            temp_path = f.name

        try:
            result = await read_geo_file(temp_path, limit=3)
            assert result["success"] is True
            assert result["data"]["feature_count"] == 3
        finally:
            os.unlink(temp_path)

    @pytest.mark.asyncio
    async def test_read_empty_file(self):
        """Test reading file with no features."""
        geojson_content = {
            "type": "FeatureCollection",
            "features": []
        }

        with tempfile.NamedTemporaryFile(suffix=".geojson", delete=False, mode="w") as f:
            json.dump(geojson_content, f)
            temp_path = f.name

        try:
            result = await read_geo_file(temp_path)
            assert result["success"] is False
            assert "no features" in result["error"].lower()
        finally:
            os.unlink(temp_path)


class TestWriteGeoFile:
    """Tests for writing geospatial files."""

    @pytest.mark.asyncio
    async def test_invalid_driver(self):
        """Test with invalid driver."""
        features = {
            "type": "FeatureCollection",
            "features": [
                {
                    "type": "Feature",
                    "geometry": {"type": "Point", "coordinates": [0, 0]},
                    "properties": {}
                }
            ]
        }

        result = await write_geo_file(features, "/tmp/test.geojson", driver="InvalidDriver")
        assert result["success"] is False
        assert "invalid" in result["error"].lower()

    @pytest.mark.asyncio
    async def test_invalid_features_type(self):
        """Test with non-dict features."""
        result = await write_geo_file("not a dict", "/tmp/test.geojson")
        assert result["success"] is False
        assert "geojson" in result["error"].lower()

    @pytest.mark.asyncio
    async def test_not_feature_collection(self):
        """Test with wrong GeoJSON type."""
        features = {
            "type": "Feature",  # Should be FeatureCollection
            "geometry": {"type": "Point", "coordinates": [0, 0]},
            "properties": {}
        }

        result = await write_geo_file(features, "/tmp/test.geojson")
        assert result["success"] is False
        assert "featurecollection" in result["error"].lower()

    @pytest.mark.asyncio
    async def test_empty_features(self):
        """Test with empty feature collection."""
        features = {
            "type": "FeatureCollection",
            "features": []
        }

        result = await write_geo_file(features, "/tmp/test.geojson")
        assert result["success"] is False
        assert "no features" in result["error"].lower()

    @pytest.mark.asyncio
    async def test_write_valid_geojson(self):
        """Test writing valid GeoJSON."""
        features = {
            "type": "FeatureCollection",
            "features": [
                {
                    "type": "Feature",
                    "geometry": {"type": "Point", "coordinates": [2.3522, 48.8566]},
                    "properties": {"name": "Paris"}
                },
                {
                    "type": "Feature",
                    "geometry": {"type": "Point", "coordinates": [-0.1278, 51.5074]},
                    "properties": {"name": "London"}
                }
            ]
        }

        with tempfile.TemporaryDirectory() as temp_dir:
            output_path = os.path.join(temp_dir, "output.geojson")

            result = await write_geo_file(features, output_path, driver="GeoJSON")

            assert result["success"] is True
            assert result["data"]["feature_count"] == 2
            assert result["data"]["driver"] == "GeoJSON"
            assert os.path.exists(output_path)

            # Verify written content
            with open(output_path) as f:
                written = json.load(f)
                assert written["type"] == "FeatureCollection"
                assert len(written["features"]) == 2

    @pytest.mark.asyncio
    async def test_write_creates_parent_directory(self):
        """Test that write creates parent directories."""
        features = {
            "type": "FeatureCollection",
            "features": [
                {
                    "type": "Feature",
                    "geometry": {"type": "Point", "coordinates": [0, 0]},
                    "properties": {}
                }
            ]
        }

        with tempfile.TemporaryDirectory() as temp_dir:
            output_path = os.path.join(temp_dir, "subdir", "nested", "output.geojson")

            result = await write_geo_file(features, output_path)

            assert result["success"] is True
            assert os.path.exists(output_path)

    @pytest.mark.asyncio
    async def test_write_shapefile(self):
        """Test writing Shapefile."""
        features = {
            "type": "FeatureCollection",
            "features": [
                {
                    "type": "Feature",
                    "geometry": {"type": "Point", "coordinates": [2.3522, 48.8566]},
                    "properties": {"name": "Paris"}
                }
            ]
        }

        with tempfile.TemporaryDirectory() as temp_dir:
            output_path = os.path.join(temp_dir, "output.shp")

            result = await write_geo_file(features, output_path, driver="ESRI Shapefile")

            assert result["success"] is True
            assert result["data"]["driver"] == "ESRI Shapefile"
            assert os.path.exists(output_path)

    @pytest.mark.asyncio
    async def test_write_geopackage(self):
        """Test writing GeoPackage."""
        features = {
            "type": "FeatureCollection",
            "features": [
                {
                    "type": "Feature",
                    "geometry": {"type": "Point", "coordinates": [2.3522, 48.8566]},
                    "properties": {"name": "Paris"}
                }
            ]
        }

        with tempfile.TemporaryDirectory() as temp_dir:
            output_path = os.path.join(temp_dir, "output.gpkg")

            result = await write_geo_file(features, output_path, driver="GPKG")

            assert result["success"] is True
            assert result["data"]["driver"] == "GPKG"
            assert os.path.exists(output_path)


class TestRoundTrip:
    """Tests for read/write round-trip."""

    @pytest.mark.asyncio
    async def test_geojson_round_trip(self):
        """Test reading back written GeoJSON."""
        original_features = {
            "type": "FeatureCollection",
            "features": [
                {
                    "type": "Feature",
                    "geometry": {"type": "Point", "coordinates": [2.3522, 48.8566]},
                    "properties": {"name": "Paris", "population": 2161000}
                },
                {
                    "type": "Feature",
                    "geometry": {
                        "type": "Polygon",
                        "coordinates": [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]]
                    },
                    "properties": {"name": "Square"}
                }
            ]
        }

        with tempfile.TemporaryDirectory() as temp_dir:
            output_path = os.path.join(temp_dir, "roundtrip.geojson")

            # Write
            write_result = await write_geo_file(original_features, output_path)
            assert write_result["success"] is True

            # Read back
            read_result = await read_geo_file(output_path)
            assert read_result["success"] is True
            assert read_result["data"]["feature_count"] == 2

            # Verify geometry types preserved
            geom_types = read_result["metadata"]["geometry_types"]
            assert "Point" in geom_types
            assert "Polygon" in geom_types
