from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, func
from typing import Optional, Dict, List, Any
from datetime import datetime, timedelta
from ..models.workflow import (
    Workflow, WorkflowInstance, Task, TaskComment,
    WorkflowType, WorkflowStatus, WorkflowTrigger, TaskStatus, TaskPriority
)
from ..models.booking import Booking, BookingStatus
from ..models.room import Room
from ..models.user import User
import logging

logger = logging.getLogger(__name__)

class WorkflowService:
    """Workflow Automation & Task Management Service"""
    
    @staticmethod
    def create_workflow(
        db: Session,
        name: str,
        workflow_type: WorkflowType,
        trigger: WorkflowTrigger,
        steps: List[Dict[str, Any]],
        created_by: int,
        description: Optional[str] = None,
        trigger_config: Optional[Dict[str, Any]] = None,
        sla_hours: Optional[int] = None
    ) -> Workflow:
        """Create a new workflow"""
        workflow = Workflow(
            name=name,
            description=description,
            workflow_type=workflow_type,
            trigger=trigger,
            trigger_config=trigger_config or {},
            steps=steps,
            sla_hours=sla_hours,
            created_by=created_by,
            status=WorkflowStatus.active,
            is_active=True
        )
        db.add(workflow)
        db.commit()
        db.refresh(workflow)
        return workflow
    
    @staticmethod
    def get_workflows(
        db: Session,
        workflow_type: Optional[WorkflowType] = None,
        status: Optional[WorkflowStatus] = None,
        skip: int = 0,
        limit: int = 100
    ) -> List[Workflow]:
        """Get workflows with optional filters"""
        query = db.query(Workflow)
        
        if workflow_type:
            query = query.filter(Workflow.workflow_type == workflow_type)
        if status:
            query = query.filter(Workflow.status == status)
        
        return query.filter(Workflow.is_active == True).offset(skip).limit(limit).all()
    
    @staticmethod
    def get_workflow_by_id(db: Session, workflow_id: int) -> Optional[Workflow]:
        """Get workflow by ID"""
        return db.query(Workflow).filter(Workflow.id == workflow_id).first()
    
    @staticmethod
    def update_workflow(
        db: Session,
        workflow_id: int,
        name: Optional[str] = None,
        description: Optional[str] = None,
        steps: Optional[List[Dict[str, Any]]] = None,
        status: Optional[WorkflowStatus] = None,
        trigger_config: Optional[Dict[str, Any]] = None,
        sla_hours: Optional[int] = None
    ) -> Optional[Workflow]:
        """Update workflow"""
        workflow = db.query(Workflow).filter(Workflow.id == workflow_id).first()
        if not workflow:
            return None
        
        if name is not None:
            workflow.name = name
        if description is not None:
            workflow.description = description
        if steps is not None:
            workflow.steps = steps
        if status is not None:
            workflow.status = status
        if trigger_config is not None:
            workflow.trigger_config = trigger_config
        if sla_hours is not None:
            workflow.sla_hours = sla_hours
        
        workflow.updated_at = datetime.utcnow()
        db.commit()
        db.refresh(workflow)
        return workflow
    
    @staticmethod
    def delete_workflow(db: Session, workflow_id: int) -> bool:
        """Soft delete workflow"""
        workflow = db.query(Workflow).filter(Workflow.id == workflow_id).first()
        if not workflow:
            return False
        
        workflow.is_active = False
        workflow.status = WorkflowStatus.archived
        workflow.updated_at = datetime.utcnow()
        db.commit()
        return True
    
    @staticmethod
    def trigger_workflow(
        db: Session,
        workflow_id: int,
        booking_id: Optional[int] = None,
        room_id: Optional[int] = None,
        user_id: Optional[int] = None,
        metadata: Optional[Dict[str, Any]] = None
    ) -> Optional[WorkflowInstance]:
        """Trigger a workflow and create an instance"""
        workflow = db.query(Workflow).filter(
            and_(Workflow.id == workflow_id, Workflow.is_active == True)
        ).first()
        
        if not workflow or workflow.status != WorkflowStatus.active:
            return None
        
        # Calculate due date based on SLA
        due_date = None
        if workflow.sla_hours:
            due_date = datetime.utcnow() + timedelta(hours=workflow.sla_hours)
        
        # Create workflow instance
        instance = WorkflowInstance(
            workflow_id=workflow_id,
            booking_id=booking_id,
            room_id=room_id,
            user_id=user_id,
            status='pending',
            due_date=due_date,
            meta_data=metadata or {}
        )
        db.add(instance)
        db.commit()
        db.refresh(instance)
        
        # Create tasks from workflow steps
        WorkflowService._create_tasks_from_workflow(db, instance, workflow, created_by=workflow.created_by)
        
        return instance
    
    @staticmethod
    def _create_tasks_from_workflow(
        db: Session,
        instance: WorkflowInstance,
        workflow: Workflow,
        created_by: int
    ):
        """Create tasks from workflow steps"""
        for step in workflow.steps:
            task = Task(
                title=step.get('title', 'Untitled Task'),
                description=step.get('description'),
                task_type=step.get('task_type', 'general'),
                priority=TaskPriority(step.get('priority', 'medium')),
                workflow_instance_id=instance.id,
                booking_id=instance.booking_id,
                room_id=instance.room_id,
                assigned_to=step.get('assigned_to'),
                created_by=created_by,
                due_date=WorkflowService._calculate_task_due_date(step, instance.due_date),
                estimated_duration_minutes=step.get('estimated_duration_minutes'),
                meta_data=step.get('metadata', {})
            )
            db.add(task)
        
        db.commit()
    
    @staticmethod
    def _calculate_task_due_date(step: Dict[str, Any], workflow_due_date: Optional[datetime]) -> Optional[datetime]:
        """Calculate task due date based on step configuration"""
        if not workflow_due_date:
            return None
        
        offset_hours = step.get('due_date_offset_hours', 0)
        return workflow_due_date - timedelta(hours=offset_hours)
    
    @staticmethod
    def get_workflow_instances(
        db: Session,
        workflow_id: Optional[int] = None,
        booking_id: Optional[int] = None,
        status: Optional[str] = None,
        skip: int = 0,
        limit: int = 100
    ) -> List[WorkflowInstance]:
        """Get workflow instances with optional filters"""
        query = db.query(WorkflowInstance)
        
        if workflow_id:
            query = query.filter(WorkflowInstance.workflow_id == workflow_id)
        if booking_id:
            query = query.filter(WorkflowInstance.booking_id == booking_id)
        if status:
            query = query.filter(WorkflowInstance.status == status)
        
        return query.order_by(WorkflowInstance.created_at.desc()).offset(skip).limit(limit).all()
    
    @staticmethod
    def complete_workflow_instance(db: Session, instance_id: int) -> Optional[WorkflowInstance]:
        """Mark workflow instance as completed"""
        instance = db.query(WorkflowInstance).filter(WorkflowInstance.id == instance_id).first()
        if not instance:
            return None
        
        # Check if all tasks are completed
        incomplete_tasks = db.query(Task).filter(
            and_(
                Task.workflow_instance_id == instance_id,
                Task.status != TaskStatus.completed,
                Task.status != TaskStatus.cancelled
            )
        ).count()
        
        if incomplete_tasks > 0:
            instance.status = 'in_progress'
        else:
            instance.status = 'completed'
            instance.completed_at = datetime.utcnow()
        
        instance.updated_at = datetime.utcnow()
        db.commit()
        db.refresh(instance)
        return instance
    
    @staticmethod
    def get_pre_arrival_workflows(db: Session) -> List[Workflow]:
        """Get pre-arrival workflows"""
        return db.query(Workflow).filter(
            and_(
                Workflow.workflow_type == WorkflowType.pre_arrival,
                Workflow.status == WorkflowStatus.active,
                Workflow.is_active == True
            )
        ).all()
    
    @staticmethod
    def get_room_preparation_workflows(db: Session) -> List[Workflow]:
        """Get room preparation workflows"""
        return db.query(Workflow).filter(
            and_(
                Workflow.workflow_type == WorkflowType.room_preparation,
                Workflow.status == WorkflowStatus.active,
                Workflow.is_active == True
            )
        ).all()
    
    @staticmethod
    def auto_trigger_workflows_for_booking(db: Session, booking: Booking):
        """Automatically trigger workflows for a booking based on trigger conditions"""
        # Get workflows that should be triggered for this booking
        workflows = db.query(Workflow).filter(
            and_(
                Workflow.is_active == True,
                Workflow.status == WorkflowStatus.active,
                or_(
                    Workflow.trigger == WorkflowTrigger.booking_created,
                    Workflow.trigger == WorkflowTrigger.booking_confirmed
                )
            )
        ).all()
        
        for workflow in workflows:
            # Check trigger conditions
            if WorkflowService._should_trigger_workflow(workflow, booking):
                WorkflowService.trigger_workflow(
                    db=db,
                    workflow_id=workflow.id,
                    booking_id=booking.id,
                    room_id=booking.room_id,
                    user_id=booking.user_id,
                    meta_data={'booking_status': booking.status.value if hasattr(booking.status, 'value') else str(booking.status)}
                )
    
    @staticmethod
    def _should_trigger_workflow(workflow: Workflow, booking: Booking) -> bool:
        """Check if workflow should be triggered for a booking"""
        trigger_config = workflow.trigger_config or {}
        
        # Check booking status filter
        if 'booking_status' in trigger_config:
            required_status = trigger_config['booking_status']
            booking_status = booking.status.value if hasattr(booking.status, 'value') else str(booking.status)
            if booking_status != required_status:
                return False
        
        # Check time-based triggers (e.g., X hours before check-in)
        if 'hours_before_checkin' in trigger_config:
            hours_before = trigger_config['hours_before_checkin']
            if booking.check_in_date:
                trigger_time = booking.check_in_date - timedelta(hours=hours_before)
                if datetime.utcnow() < trigger_time:
                    return False
        
        return True

