"""
Notification Service
Push notifications, SMS, email, and in-app notifications
"""
from datetime import datetime
from typing import Optional, List
import logging

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, func

from app.models.notification import Notification, NotificationType, NotificationChannel
from app.models.user import User
from app.config import settings

logger = logging.getLogger(__name__)


class NotificationService:
    """Notification management service"""
    
    @classmethod
    async def create_notification(
        cls,
        db: AsyncSession,
        user_id: int,
        title: str,
        body: str,
        notification_type: NotificationType = NotificationType.GENERAL,
        channel: NotificationChannel = NotificationChannel.IN_APP,
        ride_id: Optional[int] = None,
        action_type: Optional[str] = None,
        action_data: Optional[dict] = None,
        image_url: Optional[str] = None,
        send_push: bool = True
    ) -> Notification:
        """Create and optionally send a notification"""
        notification = Notification(
            user_id=user_id,
            title=title,
            body=body,
            notification_type=notification_type,
            channel=channel,
            ride_id=ride_id,
            action_type=action_type,
            action_data=action_data,
            image_url=image_url,
            sent_at=datetime.utcnow() if send_push else None,
            is_sent=send_push
        )
        
        db.add(notification)
        await db.commit()
        await db.refresh(notification)
        
        # Send push notification if requested
        if send_push and channel in [NotificationChannel.PUSH, NotificationChannel.IN_APP]:
            await cls._send_push_notification(db, user_id, title, body, action_data)
        
        return notification
    
    @classmethod
    async def _send_push_notification(
        cls,
        db: AsyncSession,
        user_id: int,
        title: str,
        body: str,
        data: Optional[dict] = None
    ) -> bool:
        """Send push notification via FCM"""
        # Get user's device token
        query = select(User).where(User.id == user_id)
        result = await db.execute(query)
        user = result.scalar_one_or_none()
        
        if not user or not user.device_token:
            logger.warning(f"Cannot send push: user {user_id} has no device token")
            return False
        
        # TODO: Implement actual Firebase push notification
        # For now, just log it
        logger.info(f"Push notification to user {user_id}: {title}")
        
        return True
    
    @classmethod
    async def send_sms(
        cls,
        phone: str,
        message: str
    ) -> bool:
        """Send SMS notification via Twilio"""
        # TODO: Implement Twilio SMS sending
        logger.info(f"SMS to {phone}: {message}")
        return True
    
    @classmethod
    async def send_email(
        cls,
        email: str,
        subject: str,
        body: str,
        html: Optional[str] = None
    ) -> bool:
        """Send email notification"""
        # TODO: Implement email sending via SMTP
        logger.info(f"Email to {email}: {subject}")
        return True
    
    @classmethod
    async def get_user_notifications(
        cls,
        db: AsyncSession,
        user_id: int,
        unread_only: bool = False,
        limit: int = 20,
        offset: int = 0
    ) -> List[Notification]:
        """Get user's notifications"""
        query = select(Notification).where(
            Notification.user_id == user_id
        )
        
        if unread_only:
            query = query.where(Notification.is_read == False)
        
        query = query.order_by(Notification.created_at.desc()).offset(offset).limit(limit)
        
        result = await db.execute(query)
        return list(result.scalars().all())
    
    @classmethod
    async def mark_as_read(
        cls,
        db: AsyncSession,
        notification_id: int,
        user_id: int
    ) -> bool:
        """Mark notification as read"""
        query = select(Notification).where(
            Notification.id == notification_id,
            Notification.user_id == user_id
        )
        result = await db.execute(query)
        notification = result.scalar_one_or_none()
        
        if not notification:
            return False
        
        notification.is_read = True
        notification.read_at = datetime.utcnow()
        await db.commit()
        
        return True
    
    @classmethod
    async def mark_all_as_read(
        cls,
        db: AsyncSession,
        user_id: int
    ) -> int:
        """Mark all notifications as read"""
        query = select(Notification).where(
            Notification.user_id == user_id,
            Notification.is_read == False
        )
        result = await db.execute(query)
        notifications = result.scalars().all()
        
        count = 0
        for notification in notifications:
            notification.is_read = True
            notification.read_at = datetime.utcnow()
            count += 1
        
        await db.commit()
        return count
    
    @classmethod
    async def get_unread_count(
        cls,
        db: AsyncSession,
        user_id: int
    ) -> int:
        """Get count of unread notifications"""
        query = select(func.count()).select_from(Notification).where(
            Notification.user_id == user_id,
            Notification.is_read == False
        )
        result = await db.execute(query)
        return result.scalar() or 0
    
    # Convenience methods for common notification types
    
    @classmethod
    async def notify_ride_accepted(
        cls,
        db: AsyncSession,
        passenger_id: int,
        driver_name: str,
        ride_id: int
    ):
        """Notify passenger that ride was accepted"""
        return await cls.create_notification(
            db,
            user_id=passenger_id,
            title="Ride Accepted",
            body=f"{driver_name} has accepted your ride request",
            notification_type=NotificationType.RIDE_ACCEPTED,
            ride_id=ride_id,
            action_type="open_ride",
            action_data={"ride_id": ride_id}
        )
    
    @classmethod
    async def notify_driver_arrived(
        cls,
        db: AsyncSession,
        passenger_id: int,
        ride_id: int
    ):
        """Notify passenger that driver has arrived"""
        return await cls.create_notification(
            db,
            user_id=passenger_id,
            title="Driver Arrived",
            body="Your driver has arrived at the pickup location",
            notification_type=NotificationType.DRIVER_ARRIVED,
            ride_id=ride_id,
            action_type="open_ride",
            action_data={"ride_id": ride_id}
        )
    
    @classmethod
    async def notify_new_ride_request(
        cls,
        db: AsyncSession,
        driver_id: int,
        ride_id: int,
        pickup_address: str,
        estimated_fare: int
    ):
        """Notify driver of new ride request in area"""
        return await cls.create_notification(
            db,
            user_id=driver_id,
            title="New Ride Request",
            body=f"New ride request from {pickup_address}",
            notification_type=NotificationType.RIDE_REQUEST,
            ride_id=ride_id,
            action_type="view_ride_request",
            action_data={"ride_id": ride_id, "fare": estimated_fare}
        )
    
    @classmethod
    async def notify_new_bid(
        cls,
        db: AsyncSession,
        passenger_id: int,
        ride_id: int,
        driver_name: str,
        bid_amount: int
    ):
        """Notify passenger of new fare bid"""
        return await cls.create_notification(
            db,
            user_id=passenger_id,
            title="New Fare Bid",
            body=f"{driver_name} has submitted a bid of ${bid_amount/100:.2f}",
            notification_type=NotificationType.NEW_BID,
            ride_id=ride_id,
            action_type="view_bids",
            action_data={"ride_id": ride_id}
        )
