import unittest
from unittest.mock import patch, MagicMock
import numpy as np
from geodata_pipeline.buildings import export_buildings
from geodata_pipeline.config import Config

class TestBuildingsMemory(unittest.TestCase):
    @patch("geodata_pipeline.buildings.gdal.Open")
    @patch("geodata_pipeline.buildings._ensure_cityjson_for_tile")
    @patch("geodata_pipeline.buildings._load_cityjson")
    @patch("geodata_pipeline.buildings._collect_faces")
    @patch("geodata_pipeline.buildings._load_ortho")
    @patch("geodata_pipeline.buildings._compose_glb")
    @patch("geodata_pipeline.buildings.ensure_dir")
    @patch("os.path.exists")
    @patch("builtins.open")
    def test_ground_snapping_windowed_read(self, mock_open_file, mock_exists, mock_ensure_dir, mock_compose, mock_load_ortho, mock_collect, mock_load_cj, mock_ensure_cj, mock_gdal_open):
        # Setup mocks
        mock_exists.return_value = True
        
        # Mock file handle for manifest CSV
        mock_handle = MagicMock()
        mock_open_file.return_value.__enter__.return_value = mock_handle
        mock_handle.__iter__.return_value = [
            "tile_id,xmin,ymin,xmax,ymax,global_min,global_max,out_res,tile_key,tile_min,tile_max\n",
            "tile1,1000,1000,2000,2000,0,100,1025,1_1,0,100\n"
        ]
        
        mock_ensure_cj.return_value = "dummy.json"
        mock_load_cj.return_value = {"CityObjects": {}}
        mock_collect.return_value = (
            [[10.0, 10.0, 5.0]], # vertices (local to tile)
            [([0, 0, 0], "WallSurface")] # faces
        )
        mock_load_ortho.return_value = None
        
        # Mock GDAL for heightmap
        mock_ds = MagicMock()
        mock_gdal_open.return_value = mock_ds
        mock_ds.GetGeoTransform.return_value = (0, 1, 0, 3000, 0, -1) # Origin (0, 3000), 1m resolution
        mock_ds.RasterXSize = 5000
        mock_ds.RasterYSize = 5000
        
        mock_band = MagicMock()
        mock_ds.GetRasterBand.return_value = mock_band
        mock_band.GetNoDataValue.return_value = -9999
        mock_band.ReadAsArray.return_value = np.array([[50.0, 50.0], [50.0, 50.0]]) # Return 2x2 array
        
        cfg = Config.default()
        cfg.work.heightmap_vrt = "dgm.vrt"
        cfg.export.manifest_path = "manifest.csv"
        
        export_buildings(cfg)
        
        # Verify ReadAsArray was called with a window
        args, kwargs = mock_band.ReadAsArray.call_args
        # ReadAsArray(xoff, yoff, xsize, ysize)
        self.assertEqual(len(args), 4)
        xoff, yoff, xsize, ysize = args
        # tile1 starts at 1000, 1000 (xmin, ymin) -> (xmin, ymax) = (1000, 2000) for GDAL
        # xoff = (1000 - 0) / 1 = 1000
        # yoff = (2000 - 3000) / -1 = 1000
        self.assertEqual(xoff, 1000)
        self.assertEqual(yoff, 1000)

if __name__ == "__main__":
    unittest.main()