# services/external/app/repositories/weather_repository.py

from typing import List, Dict, Any, Optional
from datetime import datetime
from sqlalchemy import select, and_
from sqlalchemy.ext.asyncio import AsyncSession
import structlog
import json

from app.models.weather import WeatherData

logger = structlog.get_logger()

class WeatherRepository:
    """
    Repository for weather data operations, adapted for WeatherService.
    """

    def __init__(self, session: AsyncSession):
        self.session = session

    async def get_historical_weather(self,
                                   location_id: str,
                                   start_date: datetime,
                                   end_date: datetime) -> List[WeatherData]:
        """
        Retrieves historical weather data for a specific location and date range.
        This method directly supports the data retrieval logic in WeatherService.
        """
        try:
            stmt = select(WeatherData).where(
                and_(
                    WeatherData.location_id == location_id,
                    WeatherData.date >= start_date,
                    WeatherData.date <= end_date
                )
            ).order_by(WeatherData.date)

            result = await self.session.execute(stmt)
            records = result.scalars().all()
            logger.debug(f"Retrieved {len(records)} historical records for location {location_id}")
            return list(records)

        except Exception as e:
            logger.error(
                "Failed to get historical weather from repository",
                error=str(e),
                location_id=location_id
            )
            raise

    def _serialize_json_fields(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """
        Serialize JSON fields (raw_data, processed_data) to ensure proper JSON storage
        """
        serialized = data.copy()
        
        # Serialize raw_data if present
        if 'raw_data' in serialized and serialized['raw_data'] is not None:
            if not isinstance(serialized['raw_data'], str):
                try:
                    # Convert datetime objects to strings for JSON serialization
                    raw_data = serialized['raw_data']
                    if isinstance(raw_data, dict):
                        # Handle datetime objects in the dict
                        json_safe_data = {}
                        for k, v in raw_data.items():
                            if hasattr(v, 'isoformat'):  # datetime-like object
                                json_safe_data[k] = v.isoformat()
                            else:
                                json_safe_data[k] = v
                        serialized['raw_data'] = json_safe_data
                except Exception as e:
                    logger.warning(f"Could not serialize raw_data, storing as string: {e}")
                    serialized['raw_data'] = str(raw_data)
        
        # Serialize processed_data if present  
        if 'processed_data' in serialized and serialized['processed_data'] is not None:
            if not isinstance(serialized['processed_data'], str):
                try:
                    processed_data = serialized['processed_data']
                    if isinstance(processed_data, dict):
                        json_safe_data = {}
                        for k, v in processed_data.items():
                            if hasattr(v, 'isoformat'):  # datetime-like object
                                json_safe_data[k] = v.isoformat()
                            else:
                                json_safe_data[k] = v
                        serialized['processed_data'] = json_safe_data
                except Exception as e:
                    logger.warning(f"Could not serialize processed_data, storing as string: {e}")
                    serialized['processed_data'] = str(processed_data)
        
        return serialized

    async def bulk_create_weather_data(self, weather_records: List[Dict[str, Any]]) -> None:
        """
        Bulk inserts new weather records into the database.
        Used by WeatherService after fetching new historical data from an external API.
        """
        try:
            if not weather_records:
                return

            # Serialize JSON fields before creating model instances
            serialized_records = [self._serialize_json_fields(data) for data in weather_records]
            records = [WeatherData(**data) for data in serialized_records]
            self.session.add_all(records)
            await self.session.commit()
            logger.info(f"Successfully bulk inserted {len(records)} weather records")

        except Exception as e:
            await self.session.rollback()
            logger.error(
                "Failed to bulk create weather records",
                error=str(e),
                count=len(weather_records)
            )
            raise

    async def create_weather_data(self, data: Dict[str, Any]) -> WeatherData:
        """
        Creates a single new weather data record.
        """
        try:
            # Serialize JSON fields before creating model instance
            serialized_data = self._serialize_json_fields(data)
            new_record = WeatherData(**serialized_data)
            self.session.add(new_record)
            await self.session.commit()
            await self.session.refresh(new_record)
            logger.info(f"Created new weather record with ID {new_record.id}")
            return new_record
            
        except Exception as e:
            await self.session.rollback()
            logger.error("Failed to create single weather record", error=str(e))
            raise