# app/services/notification_service.py
from typing import List, Optional, Dict, Any
from sqlalchemy.orm import Session
from app.models.notification import Notification
from app.models.device_token import DeviceToken
from app.schemas.notification import NotificationResponse
import logging
from datetime import datetime
from zoneinfo import ZoneInfo
from fastapi import HTTPException
from sqlalchemy import text
from app.core.time import now
from app.core.expo_push import send_push_notification
from app.core.firebase_config import send_firebase_message, send_firebase_multicast, initialize_firebase
from app.services.device_token_service import DeviceTokenService
import json

logger = logging.getLogger('adimsayar')

class NotificationService:
    @staticmethod
    def get_notifications(db: Session, user_id: int) -> List[NotificationResponse]:
        """
        사용자의 알림을 조회합니다 (소프트 삭제된 알림은 제외)
        """
        # data 필드에서 'deleted': true가 있는 알림 제외
        notifications = db.query(Notification).filter(
            Notification.user_id == user_id,
            # MySQL 호환 쿼리: data 필드가 NULL이거나 deleted:true가 없는 경우만 선택
            text("(data IS NULL OR JSON_EXTRACT(data, '$.deleted') IS NULL OR JSON_EXTRACT(data, '$.deleted') = 'false')")
        ).all()
        
        logger.info(f"get_notifications: found {len(notifications)} notifications for user_id {user_id}")
        # 디버깅용 로깅 추가
        for n in notifications:
            logger.debug(f"Notification id: {n.id}, data: {n.data}")
        
        return [NotificationResponse.from_orm(notification) for notification in notifications]

    @staticmethod
    def mark_notification_as_read(db: Session, notification_id: int, user_id: int) -> Optional[Notification]:
        # Filter to update only notifications owned by the user.
        notification = db.query(Notification).filter(
            Notification.id == notification_id,
            Notification.user_id == user_id
        ).first()
        if notification is None:
            logger.warning(f"Notification with id {notification_id} not found or not authorized for user_id {user_id}")
            return None
        notification.read_at = datetime.now(ZoneInfo("Europe/Istanbul"))
        db.commit()
        db.refresh(notification)
        logger.info(f"Notification id {notification_id} marked as read for user_id {user_id}")
        return notification

    @staticmethod
    def create_notification(db: Session, user_id: int, notif_type: str, title: str, message: str, data: Dict[str, Any] = None) -> Notification:
        """
        알림을 생성하고 데이터베이스에 추가합니다. 
        주의: 이 메서드는 db.commit()을 호출하지 않으므로, 호출하는 쪽에서 처리해야 합니다.
        
        푸시 알림 기능을 위해 호출하는 쪽에서 선택적으로 커밋을 제어할 수 있도록 설계되었습니다.
        """
        new_notification = Notification(
            user_id=user_id,
            type=notif_type,
            title=title,
            message=message,
            data=data
        )
        db.add(new_notification)
        return new_notification

    @staticmethod
    def send_notification(db: Session, user_id: int, title: str, message: str, notif_type: str = "general", data: Dict[str, Any] = None, send_push: bool = True):
        """
        알림을 생성하고 데이터베이스에 저장하며, 선택적으로 푸시 알림을 발송합니다.
        Firebase와 Expo 모두 지원합니다.
        """
        # 알림 생성 및 저장
        new_notification = Notification(
            user_id=user_id,
            type=notif_type,
            title=title,
            message=message,
            data=data
        )
        db.add(new_notification)
        db.commit()
        db.refresh(new_notification)
        
        logger.info(f"New notification saved to database for user_id {user_id} with ID {new_notification.id}")
        
        # 푸시 알림 전송 (옵션이 활성화된 경우)
        if send_push:
            try:
                # Firebase 초기화
                initialize_firebase()
                
                # 사용자의 디바이스 토큰 조회
                device_tokens = DeviceTokenService.get_user_device_tokens(db, user_id)
                
                if not device_tokens:
                    logger.info(f"No active device tokens found for user_id {user_id}")
                    return new_notification
                
                # Firebase FCM 토큰과 Expo 토큰을 분리
                fcm_tokens = []
                expo_tokens = []
                
                for token in device_tokens:
                    if token.device_token.startswith('ExponentPushToken['):
                        expo_tokens.append(token.device_token)
                    else:
                        fcm_tokens.append(token.device_token)
                
                # 데이터에 기본 필드 추가
                enhanced_data = data.copy() if data else {}
                enhanced_data["notification_id"] = str(new_notification.id)
                if "type" not in enhanced_data:
                    enhanced_data["type"] = notif_type
                
                # 모든 값을 문자열로 변환 (FCM 요구사항)
                string_data = {}
                for key, value in enhanced_data.items():
                    string_data[str(key)] = str(value)
                
                # Firebase FCM으로 알림 전송
                if fcm_tokens:
                    logger.info(f"Sending Firebase push to {len(fcm_tokens)} tokens for user_id {user_id}")
                    success, failure = send_firebase_multicast(
                        tokens=fcm_tokens,
                        title=title,
                        body=message,
                        data=string_data,
                        notification_type=notif_type
                    )
                    logger.info(f"Firebase push result: {success} success, {failure} failure")
                
                # Expo로 알림 전송
                if expo_tokens:
                    logger.info(f"Sending Expo push to {len(expo_tokens)} tokens for user_id {user_id}")
                    from app.services.expo_push_notification_service import ExpoPushNotificationService
                    ExpoPushNotificationService.send_push_to_user(
                        db=db,
                        user_id=user_id,
                        title=title,
                        message=message,
                        data=string_data,
                        notification_type=notif_type,
                        save_to_db=False  # 이미 DB에 저장했으므로 중복 저장 방지
                    )
            except Exception as e:
                logger.error(f"Error sending push notification: {str(e)}")
        
        return new_notification

    @staticmethod
    def send_welcome_notification(db: Session, user_id: int):
        """사용자 로그인 시 환영 메시지 전송"""
        logger.debug(f"New Client Login – Welcome Message Sent")
        return NotificationService.send_notification(
            db=db,
            user_id=user_id,
            title="Hoş geldiniz!",
            message="Adimsayar'a hoş geldiniz. İyi vakit geçirmenizi dileriz!",
            notif_type="hoş_geliş"
        )

    @staticmethod
    def soft_delete_notification(db: Session, notification_id: int, user_id: int) -> Optional[Notification]:
        """
        알림을 소프트 삭제합니다 (MySQL JSON 필드 직접 업데이트)
        """
        # 상세 로깅 추가
        logger.info(f"Attempting to soft delete notification {notification_id} for user {user_id}")
        
        # 먼저 알림이 존재하고 해당 사용자의 것인지 확인
        notification = db.query(Notification).filter(
            Notification.id == notification_id,
            Notification.user_id == user_id
        ).first()
        
        if notification is None:
            logger.warning(f"Notification with id {notification_id} not found or not authorized for user_id {user_id}")
            return None
        
        # 기존 data 가져오기
        current_data = notification.data or {}
        logger.info(f"Current data before update: {current_data}")
        
        # 현재 시간 (ISO 형식)
        deleted_at = datetime.now().isoformat()
        
        # 기존 data에 삭제 정보 추가
        current_data["deleted"] = True
        current_data["deleted_at"] = deleted_at
        
        # JSON 문자열로 변환
        json_data = json.dumps(current_data)
        
        try:
            # MySQL JSON_SET 함수를 사용하여 data 필드 업데이트
            # 직접 원시 SQL 쿼리 실행
            result = db.execute(
                text("""
                    UPDATE notifications 
                    SET data = :json_data 
                    WHERE id = :notification_id AND user_id = :user_id
                """), 
                {
                    "json_data": json_data,
                    "notification_id": notification_id,
                    "user_id": user_id
                }
            )
            
            # 커밋
            db.commit()
            
            logger.info(f"Update result: {result.rowcount} rows affected")
            
            # 변경사항 확인을 위해 다시 조회
            updated_notification = db.query(Notification).filter(
                Notification.id == notification_id
            ).first()
            
            logger.info(f"Updated notification data: {updated_notification.data}")
            
            return updated_notification
            
        except Exception as e:
            db.rollback()
            logger.error(f"Error updating notification data: {str(e)}", exc_info=True)
            raise

    @staticmethod
    def send_firebase_notification(db: Session, user_id: int, title: str, message: str, notif_type: str = "general", data: Dict[str, Any] = None):
        """
        Firebase를 통해 푸시 알림을 발송하고 데이터베이스에 알림을 저장합니다.
        """
        try:
            # Firebase 초기화
            initialize_firebase()
            
            # 알림 생성 및 저장
            new_notification = Notification(
                user_id=user_id,
                type=notif_type,
                title=title,
                message=message,
                data=data
            )
            db.add(new_notification)
            db.commit()
            db.refresh(new_notification)
            
            logger.info(f"New notification saved to database for user_id {user_id} with ID {new_notification.id}")
            
            # 사용자의 디바이스 토큰 조회
            device_tokens = DeviceTokenService.get_user_device_tokens(db, user_id)
            
            if not device_tokens:
                logger.info(f"No active device tokens found for user_id {user_id}")
                return new_notification
            
            # Firebase FCM 토큰 필터링 (Expo 토큰 제외)
            fcm_tokens = [token.device_token for token in device_tokens 
                        if not token.device_token.startswith('ExponentPushToken[')]
            
            if not fcm_tokens:
                logger.info(f"No FCM tokens found for user_id {user_id}")
                return new_notification
            
            # 데이터에 기본 필드 추가
            enhanced_data = data.copy() if data else {}
            if "type" not in enhanced_data:
                enhanced_data["type"] = notif_type
            
            # Firebase FCM으로 알림 전송
            logger.info(f"Sending Firebase push to {len(fcm_tokens)} tokens for user_id {user_id}")
            success, failure = send_firebase_multicast(
                tokens=fcm_tokens,
                title=title,
                body=message,
                data=enhanced_data,
                notification_type=notif_type
            )
            logger.info(f"Firebase push result: {success} success, {failure} failure")
            
            return new_notification
        except Exception as e:
            logger.error(f"Error sending Firebase notification: {str(e)}")
            db.rollback()
            raise

async def create_chat_notification(
    db: Session,
    user_id: int,
    sender_name: str,
    message_content: str,
    message_id: int
) -> Notification:
    """채팅 메시지 알림 생성"""
    # 1. 알림 데이터 생성
    notification = Notification(
        user_id=user_id,
        type="chat",
        title=f"{sender_name}",
        message=message_content[:100] + ("..." if len(message_content) > 100 else ""),
        data={"message_id": message_id, "type": "chat"},
        created_at=now()
    )
    db.add(notification)
    db.commit()
    db.refresh(notification)
    
    # 2. 해당 사용자의 디바이스 토큰 조회
    device_tokens = db.query(DeviceToken).filter(
        DeviceToken.user_id == user_id,
        DeviceToken.is_active == True
    ).all()
    
    # 3. 푸시 알림 전송
    if device_tokens:
        try:
            # Firebase 초기화
            initialize_firebase()
            
            # Firebase FCM 토큰과 Expo 토큰을 분리
            fcm_tokens = []
            expo_tokens = []
            
            for token in device_tokens:
                if token.device_token.startswith('ExponentPushToken['):
                    expo_tokens.append(token.device_token)
                else:
                    fcm_tokens.append(token.device_token)
            
            # Firebase FCM으로 알림 전송
            if fcm_tokens:
                logger.info(f"Sending Firebase chat notification to {len(fcm_tokens)} tokens for user_id {user_id}")
                send_firebase_multicast(
                    tokens=fcm_tokens,
                    title=notification.title,
                    body=notification.message,
                    data=notification.data,
                    notification_type="chat"
                )
            
            # Expo로 알림 전송
            if expo_tokens:
                await send_push_notification(
                    tokens=expo_tokens,
                    title=notification.title,
                    body=notification.message,
                    data=notification.data
                )
        except Exception as e:
            logger.error(f"Error sending chat push notification: {str(e)}")
    
    return notification