"""
Enterprise AI Assistant Service
Monitors rooms, bookings, invoices, payments, and chat messages
Provides intelligent responses to queries about hotel operations
"""

from typing import Dict, List, Optional, Any
from datetime import datetime, timedelta
from sqlalchemy.orm import Session, joinedload
from sqlalchemy import and_, or_, func
import json
import logging

from ...rooms.models.room import Room, RoomStatus
from ...bookings.models.booking import Booking, BookingStatus
from ...payments.models.invoice import Invoice
from ...payments.models.payment import Payment, PaymentStatus
from ..models.chat import Chat, ChatMessage, ChatStatus
from ...auth.models.user import User
from ...shared.config.logging_config import get_logger
from .ai_knowledge_base import AIKnowledgeBase
from .ai_role_access_service import AIRoleAccessService

logger = get_logger(__name__)


class AIAssistantService:
    """Enterprise AI Assistant for Hotel Management"""
    
    def __init__(self, db: Session, current_user: Optional[User] = None):
        self.db = db
        self.current_user = current_user
        self.role_access_service = AIRoleAccessService(db, current_user) if current_user else None
    
    def get_room_status_summary(self) -> Dict[str, Any]:
        """Get comprehensive room status summary"""
        try:
            total_rooms = self.db.query(Room).count()
            available_rooms = self.db.query(Room).filter(Room.status == RoomStatus.available).count()
            occupied_rooms = self.db.query(Room).filter(Room.status == RoomStatus.occupied).count()
            maintenance_rooms = self.db.query(Room).filter(Room.status == RoomStatus.maintenance).count()
            cleaning_rooms = self.db.query(Room).filter(Room.status == RoomStatus.cleaning).count()
            
            # Get rooms with bookings today
            today = datetime.utcnow().date()
            rooms_with_bookings = self.db.query(Booking).filter(
                and_(
                    Booking.status != BookingStatus.cancelled,
                    Booking.check_in_date <= today,
                    Booking.check_out_date > today
                )
            ).count()
            
            return {
                "total_rooms": total_rooms,
                "available": available_rooms,
                "occupied": occupied_rooms,
                "maintenance": maintenance_rooms,
                "cleaning": cleaning_rooms,
                "rooms_with_active_bookings": rooms_with_bookings,
                "occupancy_rate": round((occupied_rooms / total_rooms * 100) if total_rooms > 0 else 0, 2)
            }
        except Exception as e:
            logger.error(f"Error getting room status: {str(e)}", exc_info=True)
            return {}
    
    def get_booking_summary(self, days: int = 7) -> Dict[str, Any]:
        """Get booking summary for specified days"""
        try:
            today = datetime.utcnow().date()
            end_date = today + timedelta(days=days)
            
            # Upcoming check-ins
            upcoming_checkins = self.db.query(Booking).filter(
                and_(
                    Booking.status == BookingStatus.confirmed,
                    Booking.check_in_date >= today,
                    Booking.check_in_date <= end_date
                )
            ).count()
            
            # Upcoming check-outs
            upcoming_checkouts = self.db.query(Booking).filter(
                and_(
                    Booking.status == BookingStatus.confirmed,
                    Booking.check_out_date >= today,
                    Booking.check_out_date <= end_date
                )
            ).count()
            
            # Active bookings
            active_bookings = self.db.query(Booking).filter(
                and_(
                    Booking.status == BookingStatus.confirmed,
                    Booking.check_in_date <= today,
                    Booking.check_out_date > today
                )
            ).count()
            
            # Pending bookings
            pending_bookings = self.db.query(Booking).filter(
                Booking.status == BookingStatus.pending
            ).count()
            
            return {
                "upcoming_checkins": upcoming_checkins,
                "upcoming_checkouts": upcoming_checkouts,
                "active_bookings": active_bookings,
                "pending_bookings": pending_bookings,
                "period_days": days
            }
        except Exception as e:
            logger.error(f"Error getting booking summary: {str(e)}", exc_info=True)
            return {}
    
    def get_payment_summary(self, days: int = 30) -> Dict[str, Any]:
        """Get payment summary"""
        try:
            today = datetime.utcnow().date()
            start_date = today - timedelta(days=days)
            
            # Total payments in period
            total_payments = self.db.query(func.sum(Payment.amount)).filter(
                and_(
                    Payment.payment_status == PaymentStatus.completed,
                    func.date(Payment.payment_date) >= start_date,
                    func.date(Payment.payment_date) <= today
                )
            ).scalar() or 0
            
            # Pending payments
            pending_payments = self.db.query(func.sum(Payment.amount)).filter(
                Payment.payment_status == PaymentStatus.pending
            ).scalar() or 0
            
            # Payment count
            completed_count = self.db.query(Payment).filter(
                and_(
                    Payment.payment_status == PaymentStatus.completed,
                    func.date(Payment.payment_date) >= start_date
                )
            ).count()
            
            return {
                "total_revenue": float(total_payments),
                "pending_amount": float(pending_payments),
                "completed_payments": completed_count,
                "period_days": days
            }
        except Exception as e:
            logger.error(f"Error getting payment summary: {str(e)}", exc_info=True)
            return {}
    
    def get_invoice_summary(self) -> Dict[str, Any]:
        """Get invoice summary"""
        try:
            total_invoices = self.db.query(Invoice).count()
            
            # Group by status
            status_counts = {}
            invoices = self.db.query(Invoice).all()
            for invoice in invoices:
                status = invoice.status if hasattr(invoice, 'status') else 'unknown'
                status_counts[status] = status_counts.get(status, 0) + 1
            
            # Overdue invoices
            today = datetime.utcnow().date()
            overdue_count = self.db.query(Invoice).filter(
                and_(
                    Invoice.due_date < today,
                    Invoice.status != 'paid'
                )
            ).count() if hasattr(Invoice, 'due_date') else 0
            
            return {
                "total_invoices": total_invoices,
                "status_breakdown": status_counts,
                "overdue_invoices": overdue_count
            }
        except Exception as e:
            logger.error(f"Error getting invoice summary: {str(e)}", exc_info=True)
            return {}
    
    def get_unanswered_chats(self, hours: int = 24) -> List[Dict[str, Any]]:
        """Get unanswered chat messages from after working hours"""
        try:
            cutoff_time = datetime.utcnow() - timedelta(hours=hours)
            
            # Get open chats with unread messages
            chats = self.db.query(Chat).filter(
                and_(
                    Chat.status == ChatStatus.open,
                    Chat.updated_at >= cutoff_time
                )
            ).all()
            
            unanswered = []
            for chat in chats:
                # Get last message
                last_message = self.db.query(ChatMessage).filter(
                    ChatMessage.chat_id == chat.id
                ).order_by(ChatMessage.created_at.desc()).first()
                
                if last_message and last_message.sender_type == 'visitor' and not last_message.is_read:
                    unanswered.append({
                        "chat_id": chat.id,
                        "visitor_name": chat.visitor_name,
                        "visitor_email": chat.visitor_email,
                        "last_message": last_message.message[:100],
                        "last_message_time": last_message.created_at.isoformat(),
                        "waiting_hours": (datetime.utcnow() - last_message.created_at).total_seconds() / 3600
                    })
            
            return unanswered
        except Exception as e:
            logger.error(f"Error getting unanswered chats: {str(e)}", exc_info=True)
            return []
    
    def search_occupied_rooms(self, room_number: Optional[str] = None) -> List[Dict[str, Any]]:
        """Search for occupied rooms"""
        try:
            today = datetime.utcnow().date()
            
            # Get rooms with active bookings
            query = self.db.query(Room).options(
                joinedload(Room.room_type)
            ).join(Booking).filter(
                and_(
                    Room.status == RoomStatus.occupied,
                    Booking.status == BookingStatus.confirmed,
                    Booking.check_in_date <= today,
                    Booking.check_out_date > today
                )
            )
            
            if room_number:
                query = query.filter(Room.room_number.ilike(f"%{room_number}%"))
            
            rooms = query.all()
            
            result = []
            for room in rooms:
                # Get the active booking for this room
                booking = self.db.query(Booking).options(
                    joinedload(Booking.user)
                ).filter(
                    and_(
                        Booking.room_id == room.id,
                        Booking.status == BookingStatus.confirmed,
                        Booking.check_in_date <= today,
                        Booking.check_out_date > today
                    )
                ).first()
                
                result.append({
                    "room_number": room.room_number,
                    "room_type": room.room_type.name if room.room_type else "Unknown",
                    "floor": room.floor,
                    "booking_id": booking.id if booking else None,
                    "guest_name": booking.user.full_name if booking and booking.user else "Unknown",
                    "check_in": booking.check_in_date.isoformat() if booking else None,
                    "check_out": booking.check_out_date.isoformat() if booking else None
                })
            
            return result
        except Exception as e:
            logger.error(f"Error searching occupied rooms: {str(e)}", exc_info=True)
            return []
    
    def get_room_problems(self) -> List[Dict[str, Any]]:
        """Get rooms with problems (maintenance, issues)"""
        try:
            # Rooms in maintenance
            maintenance_rooms = self.db.query(Room).filter(
                Room.status == RoomStatus.maintenance
            ).all()
            
            problems = []
            for room in maintenance_rooms:
                problems.append({
                    "room_number": room.room_number,
                    "issue_type": "maintenance",
                    "status": room.status.value,
                    "description": f"Room {room.room_number} is under maintenance"
                })
            
            # Check for maintenance records
            try:
                from ...rooms.models.room_maintenance import RoomMaintenance, MaintenanceStatus
                active_maintenance = self.db.query(RoomMaintenance).filter(
                    MaintenanceStatus.in_progress == MaintenanceStatus.in_progress
                ).all()
                
                for maint in active_maintenance:
                    room = self.db.query(Room).filter(Room.id == maint.room_id).first()
                    if room:
                        problems.append({
                            "room_number": room.room_number,
                            "issue_type": "maintenance",
                            "status": maint.status.value if hasattr(maint.status, 'value') else str(maint.status),
                            "description": maint.description or f"Maintenance issue in room {room.room_number}",
                            "scheduled_start": maint.scheduled_start.isoformat() if maint.scheduled_start else None
                        })
            except ImportError:
                pass  # Maintenance model might not exist
            
            return problems
        except Exception as e:
            logger.error(f"Error getting room problems: {str(e)}", exc_info=True)
            return []
    
    def get_booking_by_number(self, booking_number: str) -> Optional[Dict[str, Any]]:
        """Get booking details by booking number"""
        try:
            booking = self.db.query(Booking).filter(
                Booking.booking_number == booking_number
            ).first()
            
            if not booking:
                return None
            
            return {
                "booking_number": booking.booking_number,
                "status": booking.status.value if hasattr(booking.status, 'value') else str(booking.status),
                "guest_name": booking.user.full_name if booking.user else "Unknown",
                "room_number": booking.room.room_number if booking.room else "Unknown",
                "check_in": booking.check_in_date.isoformat(),
                "check_out": booking.check_out_date.isoformat(),
                "total_price": float(booking.total_price) if booking.total_price else 0,
                "payment_status": self._get_booking_payment_status(booking.id)
            }
        except Exception as e:
            logger.error(f"Error getting booking: {str(e)}", exc_info=True)
            return None
    
    def get_invoice_by_number(self, invoice_number: str) -> Optional[Dict[str, Any]]:
        """Get invoice details by invoice number"""
        try:
            invoice = self.db.query(Invoice).filter(
                Invoice.invoice_number == invoice_number
            ).first()
            
            if not invoice:
                return None
            
            return {
                "invoice_number": invoice.invoice_number,
                "status": invoice.status if hasattr(invoice, 'status') else "unknown",
                "amount": float(invoice.total_amount) if hasattr(invoice, 'total_amount') else 0,
                "due_date": invoice.due_date.isoformat() if hasattr(invoice, 'due_date') and invoice.due_date else None,
                "booking_number": invoice.booking.booking_number if invoice.booking else None,
                "created_at": invoice.created_at.isoformat() if hasattr(invoice, 'created_at') else None
            }
        except Exception as e:
            logger.error(f"Error getting invoice: {str(e)}", exc_info=True)
            return None
    
    def _get_booking_payment_status(self, booking_id: int) -> str:
        """Get payment status for a booking"""
        try:
            payments = self.db.query(Payment).filter(
                Payment.booking_id == booking_id
            ).all()
            
            if not payments:
                return "unpaid"
            
            completed = [p for p in payments if p.payment_status == PaymentStatus.completed]
            if completed:
                total_paid = sum(float(p.amount) for p in completed)
                booking = self.db.query(Booking).filter(Booking.id == booking_id).first()
                if booking and booking.total_price:
                    if total_paid >= float(booking.total_price):
                        return "paid"
                    else:
                        return "partially_paid"
            
            return "pending"
        except Exception as e:
            logger.error(f"Error getting payment status: {str(e)}", exc_info=True)
            return "unknown"
    
    def generate_context_for_ai(self) -> Dict[str, Any]:
        """Generate comprehensive context for AI assistant with role-based filtering"""
        context = {
            "timestamp": datetime.utcnow().isoformat(),
        }
        
        # Use role-based access service if user is provided
        if self.role_access_service:
            role_name = self.role_access_service.role_name
            context["user_role"] = role_name
            context["role_capabilities"] = AIKnowledgeBase.get_role_info(role_name)
            
            # Get role-appropriate data
            context["room_status"] = self.role_access_service.get_room_status_summary()
            
            # Get user-specific bookings
            context["user_bookings"] = self.role_access_service.get_user_bookings(limit=5)
            
            # Financial data only for authorized roles
            if self.role_access_service.can_access_feature("payment_summary"):
                payment_summary = self.role_access_service.get_payment_summary()
                if payment_summary:
                    context["payment_summary"] = payment_summary
                
                invoice_summary = self.role_access_service.get_invoice_summary()
                if invoice_summary:
                    context["invoice_summary"] = invoice_summary
            
            # Operational data for staff and admin
            if role_name in ["admin", "staff"]:
                context["booking_summary"] = self.get_booking_summary()
                context["unanswered_chats"] = self.get_unanswered_chats()
                context["room_problems"] = self.get_room_problems()
            
            # Add knowledge base information
            context["application_knowledge"] = {
                "features": list(AIKnowledgeBase.FEATURES.keys()),
                "role_info": AIKnowledgeBase.get_role_info(role_name)
            }
        else:
            # Default context without user (shouldn't happen in production)
            context.update({
                "room_status": self.get_room_status_summary(),
                "booking_summary": self.get_booking_summary(),
                "payment_summary": self.get_payment_summary(),
                "invoice_summary": self.get_invoice_summary(),
                "unanswered_chats": self.get_unanswered_chats(),
                "room_problems": self.get_room_problems(),
            })
        
        return context

