# services/external/tests/unit/test_repositories.py
"""
Unit tests for External Service Repositories
"""

import pytest
from datetime import datetime, timezone, timedelta
from uuid import uuid4

from app.repositories.weather_repository import WeatherRepository
from app.repositories.traffic_repository import TrafficRepository
from app.models.weather import WeatherData, WeatherStation, WeatherDataJob
from app.models.traffic import TrafficData, TrafficMeasurementPoint, TrafficDataJob


@pytest.mark.asyncio
class TestWeatherRepository:
    """Test Weather Repository operations"""

    async def test_create_weather_data(self, test_db_session, sample_weather_data):
        """Test creating weather data"""
        repository = WeatherRepository(test_db_session)
        
        record = await repository.create_weather_data(sample_weather_data)
        
        assert record is not None
        assert record.id is not None
        assert record.city == sample_weather_data["city"]
        assert record.temperature == sample_weather_data["temperature"]

    async def test_get_current_weather(self, populated_weather_db, sample_weather_data):
        """Test getting current weather data"""
        repository = WeatherRepository(populated_weather_db)
        
        result = await repository.get_current_weather("madrid")
        
        assert result is not None
        assert result.city == "madrid"
        assert result.temperature == sample_weather_data["temperature"]

    async def test_get_weather_forecast(self, test_db_session, sample_weather_forecast):
        """Test getting weather forecast"""
        repository = WeatherRepository(test_db_session)
        
        # Create forecast data
        for forecast_item in sample_weather_forecast:
            await repository.create_weather_data(forecast_item)
        
        result = await repository.get_weather_forecast("madrid", 7)
        
        assert len(result) == 1
        assert result[0].is_forecast is True

    async def test_get_historical_weather(self, test_db_session, sample_weather_data):
        """Test getting historical weather data"""
        repository = WeatherRepository(test_db_session)
        
        # Create historical data
        historical_data = sample_weather_data.copy()
        historical_data["date"] = datetime.now(timezone.utc) - timedelta(days=1)
        await repository.create_weather_data(historical_data)
        
        start_date = datetime.now(timezone.utc) - timedelta(days=2)
        end_date = datetime.now(timezone.utc)
        
        result = await repository.get_historical_weather("madrid", start_date, end_date)
        
        assert len(result) >= 1

    async def test_create_weather_station(self, test_db_session):
        """Test creating weather station"""
        repository = WeatherRepository(test_db_session)
        
        station_data = {
            "station_id": "TEST_001",
            "name": "Test Station",
            "city": "madrid",
            "latitude": 40.4168,
            "longitude": -3.7038,
            "altitude": 650.0,
            "is_active": True
        }
        
        station = await repository.create_weather_station(station_data)
        
        assert station is not None
        assert station.station_id == "TEST_001"
        assert station.name == "Test Station"

    async def test_get_weather_stations(self, test_db_session):
        """Test getting weather stations"""
        repository = WeatherRepository(test_db_session)
        
        # Create test station
        station_data = {
            "station_id": "TEST_001",
            "name": "Test Station",
            "city": "madrid",
            "latitude": 40.4168,
            "longitude": -3.7038,
            "is_active": True
        }
        await repository.create_weather_station(station_data)
        
        stations = await repository.get_weather_stations("madrid")
        
        assert len(stations) == 1
        assert stations[0].station_id == "TEST_001"

    async def test_create_weather_job(self, test_db_session, sample_tenant_id):
        """Test creating weather data collection job"""
        repository = WeatherRepository(test_db_session)
        
        job_data = {
            "job_type": "current",
            "city": "madrid",
            "status": "pending",
            "scheduled_at": datetime.utcnow(),
            "tenant_id": sample_tenant_id
        }
        
        job = await repository.create_weather_job(job_data)
        
        assert job is not None
        assert job.job_type == "current"
        assert job.status == "pending"

    async def test_update_weather_job(self, test_db_session, sample_tenant_id):
        """Test updating weather job"""
        repository = WeatherRepository(test_db_session)
        
        # Create job first
        job_data = {
            "job_type": "current",
            "city": "madrid",
            "status": "pending",
            "scheduled_at": datetime.utcnow(),
            "tenant_id": sample_tenant_id
        }
        job = await repository.create_weather_job(job_data)
        
        # Update job
        update_data = {
            "status": "completed",
            "completed_at": datetime.utcnow(),
            "success_count": 1
        }
        
        success = await repository.update_weather_job(job.id, update_data)
        
        assert success is True

    async def test_get_weather_jobs(self, test_db_session, sample_tenant_id):
        """Test getting weather jobs"""
        repository = WeatherRepository(test_db_session)
        
        # Create test job
        job_data = {
            "job_type": "forecast",
            "city": "madrid",
            "status": "completed",
            "scheduled_at": datetime.utcnow(),
            "tenant_id": sample_tenant_id
        }
        await repository.create_weather_job(job_data)
        
        jobs = await repository.get_weather_jobs()
        
        assert len(jobs) >= 1
        assert any(job.job_type == "forecast" for job in jobs)


@pytest.mark.asyncio
class TestTrafficRepository:
    """Test Traffic Repository operations"""

    async def test_create_traffic_data(self, test_db_session, sample_traffic_data):
        """Test creating traffic data"""
        repository = TrafficRepository(test_db_session)
        
        # Convert sample data to list for bulk create
        traffic_list = [sample_traffic_data]
        
        count = await repository.bulk_create_traffic_data(traffic_list)
        
        assert count == 1

    async def test_get_current_traffic(self, populated_traffic_db, sample_traffic_data):
        """Test getting current traffic data"""
        repository = TrafficRepository(populated_traffic_db)
        
        result = await repository.get_current_traffic("madrid")
        
        assert len(result) >= 1
        assert result[0].city == "madrid"

    async def test_get_current_traffic_with_filters(self, populated_traffic_db):
        """Test getting current traffic with filters"""
        repository = TrafficRepository(populated_traffic_db)
        
        result = await repository.get_current_traffic("madrid", district="Chamartín")
        
        # Should return results based on filter
        assert isinstance(result, list)

    async def test_get_historical_traffic(self, test_db_session, sample_traffic_data):
        """Test getting historical traffic data"""
        repository = TrafficRepository(test_db_session)
        
        # Create historical data
        historical_data = sample_traffic_data.copy()
        historical_data["date"] = datetime.now(timezone.utc) - timedelta(days=1)
        await repository.bulk_create_traffic_data([historical_data])
        
        start_date = datetime.now(timezone.utc) - timedelta(days=2)
        end_date = datetime.now(timezone.utc)
        
        result = await repository.get_historical_traffic("madrid", start_date, end_date)
        
        assert len(result) >= 1

    async def test_create_measurement_point(self, test_db_session):
        """Test creating traffic measurement point"""
        repository = TrafficRepository(test_db_session)
        
        point_data = {
            "point_id": "TEST_POINT_001",
            "name": "Test Measurement Point",
            "city": "madrid",
            "point_type": "TEST",
            "latitude": 40.4168,
            "longitude": -3.7038,
            "district": "Centro",
            "road_name": "Test Road",
            "is_active": True
        }
        
        point = await repository.create_measurement_point(point_data)
        
        assert point is not None
        assert point.point_id == "TEST_POINT_001"
        assert point.name == "Test Measurement Point"

    async def test_get_measurement_points(self, test_db_session):
        """Test getting measurement points"""
        repository = TrafficRepository(test_db_session)
        
        # Create test point
        point_data = {
            "point_id": "TEST_POINT_001",
            "name": "Test Point",
            "city": "madrid",
            "point_type": "TEST",
            "latitude": 40.4168,
            "longitude": -3.7038,
            "is_active": True
        }
        await repository.create_measurement_point(point_data)
        
        points = await repository.get_measurement_points("madrid")
        
        assert len(points) == 1
        assert points[0].point_id == "TEST_POINT_001"

    async def test_get_measurement_points_with_filters(self, test_db_session):
        """Test getting measurement points with filters"""
        repository = TrafficRepository(test_db_session)
        
        # Create test points with different types
        for i, point_type in enumerate(["M30", "URB", "TEST"]):
            point_data = {
                "point_id": f"TEST_POINT_{i:03d}",
                "name": f"Test Point {i}",
                "city": "madrid",
                "point_type": point_type,
                "latitude": 40.4168,
                "longitude": -3.7038,
                "is_active": True
            }
            await repository.create_measurement_point(point_data)
        
        # Filter by type
        points = await repository.get_measurement_points("madrid", road_type="M30")
        
        assert len(points) == 1
        assert points[0].point_type == "M30"

    async def test_get_traffic_analytics(self, populated_traffic_db):
        """Test getting traffic analytics"""
        repository = TrafficRepository(populated_traffic_db)
        
        analytics = await repository.get_traffic_analytics("madrid")
        
        assert isinstance(analytics, dict)
        assert "total_measurements" in analytics
        assert "average_volume" in analytics

    async def test_create_traffic_job(self, test_db_session, sample_tenant_id):
        """Test creating traffic collection job"""
        repository = TrafficRepository(test_db_session)
        
        job_data = {
            "job_type": "current",
            "city": "madrid",
            "status": "pending",
            "scheduled_at": datetime.utcnow(),
            "tenant_id": sample_tenant_id
        }
        
        job = await repository.create_traffic_job(job_data)
        
        assert job is not None
        assert job.job_type == "current"
        assert job.status == "pending"

    async def test_update_traffic_job(self, test_db_session, sample_tenant_id):
        """Test updating traffic job"""
        repository = TrafficRepository(test_db_session)
        
        # Create job first
        job_data = {
            "job_type": "current",
            "city": "madrid",
            "status": "pending",
            "scheduled_at": datetime.utcnow(),
            "tenant_id": sample_tenant_id
        }
        job = await repository.create_traffic_job(job_data)
        
        # Update job
        update_data = {
            "status": "completed",
            "completed_at": datetime.utcnow(),
            "success_count": 10
        }
        
        success = await repository.update_traffic_job(job.id, update_data)
        
        assert success is True

    async def test_get_traffic_jobs(self, test_db_session, sample_tenant_id):
        """Test getting traffic jobs"""
        repository = TrafficRepository(test_db_session)
        
        # Create test job
        job_data = {
            "job_type": "historical",
            "city": "madrid",
            "status": "completed",
            "scheduled_at": datetime.utcnow(),
            "tenant_id": sample_tenant_id
        }
        await repository.create_traffic_job(job_data)
        
        jobs = await repository.get_traffic_jobs()
        
        assert len(jobs) >= 1
        assert any(job.job_type == "historical" for job in jobs)

    async def test_bulk_create_performance(self, test_db_session):
        """Test bulk create performance"""
        repository = TrafficRepository(test_db_session)
        
        # Create large dataset
        bulk_data = []
        for i in range(100):
            data = {
                "city": "madrid",
                "location_id": f"PM_TEST_{i:03d}",
                "date": datetime.now(timezone.utc),
                "measurement_point_id": f"PM_TEST_{i:03d}",
                "measurement_point_name": f"Test Point {i}",
                "measurement_point_type": "TEST",
                "traffic_volume": 100 + i,
                "average_speed": 50.0,
                "congestion_level": "medium",
                "occupation_percentage": 50.0,
                "latitude": 40.4168,
                "longitude": -3.7038,
                "source": "test"
            }
            bulk_data.append(data)
        
        import time
        start_time = time.time()
        
        count = await repository.bulk_create_traffic_data(bulk_data)
        
        end_time = time.time()
        execution_time = end_time - start_time
        
        assert count == 100
        assert execution_time < 3.0  # Should complete in under 3 seconds