from typing import Optional, Dict, Any
from sqlalchemy.orm import Session
from models.activity import Activity, ActivityType
from models.user import User
from models.task import Task
import logging

logger = logging.getLogger(__name__)


class ActivityService:
    """Service for creating and managing activity logs."""
    
    @staticmethod
    def log_activity(
        db: Session,
        type: ActivityType,
        user_id: int,
        description: str,
        project_id: Optional[int] = None,
        task_id: Optional[int] = None,
        asset_id: Optional[int] = None,
        shot_id: Optional[int] = None,
        submission_id: Optional[int] = None,
        activity_metadata: Optional[Dict[str, Any]] = None
    ) -> Optional[Activity]:
        """Log an activity to the database."""
        try:
            activity = Activity(
                type=type,
                user_id=user_id,
                description=description,
                project_id=project_id,
                task_id=task_id,
                asset_id=asset_id,
                shot_id=shot_id,
                submission_id=submission_id,
                activity_metadata=activity_metadata
            )
            
            db.add(activity)
            db.commit()
            db.refresh(activity)
            
            logger.info(f"Activity logged: {type} by user {user_id}")
            return activity
            
        except Exception as e:
            logger.error(f"Failed to log activity: {str(e)}")
            db.rollback()
            return None
    
    @staticmethod
    def log_task_created(db: Session, task: Task, user: User):
        """Log task creation activity."""
        description = f"{user.first_name} {user.last_name} created task '{task.name}'"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.TASK_CREATED,
            user_id=user.id,
            description=description,
            project_id=task.project_id,
            task_id=task.id,
            activity_metadata={"task_type": task.task_type, "status": task.status}
        )
    
    @staticmethod
    def log_task_assigned(db: Session, task: Task, assigned_user: User, assigner: User):
        """Log task assignment activity."""
        description = f"{assigner.first_name} {assigner.last_name} assigned task '{task.name}' to {assigned_user.first_name} {assigned_user.last_name}"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.TASK_ASSIGNED,
            user_id=assigner.id,
            description=description,
            project_id=task.project_id,
            task_id=task.id,
            activity_metadata={"assigned_to": assigned_user.id}
        )
    
    @staticmethod
    def log_task_status_changed(db: Session, task: Task, old_status: str, new_status: str, user: User):
        """Log task status change activity."""
        description = f"{user.first_name} {user.last_name} changed task '{task.name}' status from {old_status} to {new_status}"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.TASK_STATUS_CHANGED,
            user_id=user.id,
            description=description,
            project_id=task.project_id,
            task_id=task.id,
            activity_metadata={"old_status": old_status, "new_status": new_status}
        )
    
    @staticmethod
    def log_submission_created(db: Session, submission, user: User):
        """Log submission creation activity."""
        description = f"{user.first_name} {user.last_name} submitted work for task '{submission.task.name}' (Version {submission.version_number})"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.SUBMISSION_CREATED,
            user_id=user.id,
            description=description,
            project_id=submission.task.project_id,
            task_id=submission.task_id,
            submission_id=submission.id,
            activity_metadata={"version": submission.version_number}
        )
    
    @staticmethod
    def log_submission_reviewed(db: Session, submission, review, reviewer: User):
        """Log submission review activity."""
        decision = "approved" if review.decision == "approved" else "requested retakes for"
        description = f"{reviewer.first_name} {reviewer.last_name} {decision} submission for task '{submission.task.name}'"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.SUBMISSION_REVIEWED,
            user_id=reviewer.id,
            description=description,
            project_id=submission.task.project_id,
            task_id=submission.task_id,
            submission_id=submission.id,
            activity_metadata={"decision": review.decision, "has_feedback": bool(review.feedback)}
        )
    
    @staticmethod
    def log_comment_added(db: Session, task: Task, user: User, comment_text: str):
        """Log comment addition activity."""
        preview = comment_text[:50] + "..." if len(comment_text) > 50 else comment_text
        description = f"{user.first_name} {user.last_name} commented on task '{task.name}': {preview}"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.COMMENT_ADDED,
            user_id=user.id,
            description=description,
            project_id=task.project_id,
            task_id=task.id
        )
    
    @staticmethod
    def log_asset_created(db: Session, asset, user: User):
        """Log asset creation activity."""
        description = f"{user.first_name} {user.last_name} created asset '{asset.name}' ({asset.category})"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.ASSET_CREATED,
            user_id=user.id,
            description=description,
            project_id=asset.project_id,
            asset_id=asset.id,
            activity_metadata={"category": asset.category}
        )
    
    @staticmethod
    def log_shot_created(db: Session, shot, user: User):
        """Log shot creation activity."""
        description = f"{user.first_name} {user.last_name} created shot '{shot.name}'"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.SHOT_CREATED,
            user_id=user.id,
            description=description,
            project_id=shot.episode.project_id if shot.episode else None,
            shot_id=shot.id
        )
    
    @staticmethod
    def log_project_created(db: Session, project, user: User):
        """Log project creation activity."""
        description = f"{user.first_name} {user.last_name} created project '{project.name}'"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.PROJECT_CREATED,
            user_id=user.id,
            description=description,
            project_id=project.id,
            activity_metadata={"project_type": project.project_type}
        )
    
    @staticmethod
    def log_user_joined_project(db: Session, project, user: User, added_by: User):
        """Log user joining project activity."""
        description = f"{added_by.first_name} {added_by.last_name} added {user.first_name} {user.last_name} to project '{project.name}'"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.USER_JOINED_PROJECT,
            user_id=added_by.id,
            description=description,
            project_id=project.id,
            activity_metadata={"added_user_id": user.id}
        )
    
    @staticmethod
    def log_shot_soft_deletion(db: Session, shot, user: User, deletion_info: Dict[str, Any]):
        """Log shot soft deletion activity."""
        description = f"{user.first_name} {user.last_name} deleted shot '{shot.name}' from episode '{deletion_info.get('episode_name', 'Unknown')}'"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.SHOT_DELETED,
            user_id=user.id,
            description=description,
            project_id=deletion_info.get('project_id'),
            shot_id=shot.id,
            activity_metadata={
                "shot_name": shot.name,
                "episode_name": deletion_info.get('episode_name'),
                "project_name": deletion_info.get('project_name'),
                "task_count": deletion_info.get('task_count', 0),
                "submission_count": deletion_info.get('submission_count', 0),
                "attachment_count": deletion_info.get('attachment_count', 0),
                "note_count": deletion_info.get('note_count', 0),
                "review_count": deletion_info.get('review_count', 0),
                "affected_users": deletion_info.get('affected_users', [])
            }
        )
    
    @staticmethod
    def log_asset_soft_deletion(db: Session, asset, user: User, deletion_info: Dict[str, Any]):
        """Log asset soft deletion activity."""
        description = f"{user.first_name} {user.last_name} deleted asset '{asset.name}' ({asset.category})"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.ASSET_DELETED,
            user_id=user.id,
            description=description,
            project_id=asset.project_id,
            asset_id=asset.id,
            activity_metadata={
                "asset_name": asset.name,
                "asset_category": asset.category,
                "project_name": deletion_info.get('project_name'),
                "task_count": deletion_info.get('task_count', 0),
                "submission_count": deletion_info.get('submission_count', 0),
                "attachment_count": deletion_info.get('attachment_count', 0),
                "note_count": deletion_info.get('note_count', 0),
                "review_count": deletion_info.get('review_count', 0),
                "affected_users": deletion_info.get('affected_users', [])
            }
        )
    
    @staticmethod
    def log_shot_recovery(db: Session, shot, user: User, recovery_info: Dict[str, Any]):
        """Log shot recovery activity."""
        description = f"{user.first_name} {user.last_name} recovered shot '{shot.name}' from episode '{recovery_info.get('episode_name', 'Unknown')}'"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.SHOT_RECOVERED,
            user_id=user.id,
            description=description,
            project_id=recovery_info.get('project_id'),
            shot_id=shot.id,
            activity_metadata={
                "shot_name": shot.name,
                "episode_name": recovery_info.get('episode_name'),
                "project_name": recovery_info.get('project_name'),
                "recovered_tasks": recovery_info.get('recovered_tasks', 0),
                "recovered_submissions": recovery_info.get('recovered_submissions', 0),
                "recovered_attachments": recovery_info.get('recovered_attachments', 0),
                "recovered_notes": recovery_info.get('recovered_notes', 0),
                "recovered_reviews": recovery_info.get('recovered_reviews', 0),
                "deleted_at": recovery_info.get('deleted_at'),
                "deleted_by": recovery_info.get('deleted_by')
            }
        )
    
    @staticmethod
    def log_asset_recovery(db: Session, asset, user: User, recovery_info: Dict[str, Any]):
        """Log asset recovery activity."""
        description = f"{user.first_name} {user.last_name} recovered asset '{asset.name}' ({asset.category})"
        ActivityService.log_activity(
            db=db,
            type=ActivityType.ASSET_RECOVERED,
            user_id=user.id,
            description=description,
            project_id=asset.project_id,
            asset_id=asset.id,
            activity_metadata={
                "asset_name": asset.name,
                "asset_category": asset.category,
                "project_name": recovery_info.get('project_name'),
                "recovered_tasks": recovery_info.get('recovered_tasks', 0),
                "recovered_submissions": recovery_info.get('recovered_submissions', 0),
                "recovered_attachments": recovery_info.get('recovered_attachments', 0),
                "recovered_notes": recovery_info.get('recovered_notes', 0),
                "recovered_reviews": recovery_info.get('recovered_reviews', 0),
                "deleted_at": recovery_info.get('deleted_at'),
                "deleted_by": recovery_info.get('deleted_by')
            }
        )
    
    @staticmethod
    def get_activities_excluding_deleted(
        db: Session,
        project_id: Optional[int] = None,
        task_id: Optional[int] = None,
        user_id: Optional[int] = None,
        skip: int = 0,
        limit: int = 50,
        type_filter: Optional[ActivityType] = None,
        days: Optional[int] = None
    ):
        """Get activities excluding those related to deleted records."""
        from sqlalchemy import and_, or_, desc
        from datetime import datetime, timedelta
        from models.task import Task
        from models.shot import Shot
        from models.asset import Asset
        from models.submission import Submission
        
        query = db.query(Activity)
        
        # Base filters
        if project_id:
            query = query.filter(Activity.project_id == project_id)
        if task_id:
            query = query.filter(Activity.task_id == task_id)
        if user_id:
            query = query.filter(Activity.user_id == user_id)
        if type_filter:
            query = query.filter(Activity.type == type_filter)
        if days:
            cutoff_date = datetime.utcnow() - timedelta(days=days)
            query = query.filter(Activity.created_at >= cutoff_date)
        
        # Exclude activities related to deleted records
        # For task-related activities, exclude if task is deleted
        task_subquery = db.query(Task.id).filter(Task.deleted_at.is_(None)).subquery()
        
        # For shot-related activities, exclude if shot is deleted
        shot_subquery = db.query(Shot.id).filter(Shot.deleted_at.is_(None)).subquery()
        
        # For asset-related activities, exclude if asset is deleted
        asset_subquery = db.query(Asset.id).filter(Asset.deleted_at.is_(None)).subquery()
        
        # For submission-related activities, exclude if submission is deleted
        submission_subquery = db.query(Submission.id).filter(Submission.deleted_at.is_(None)).subquery()
        
        # Apply exclusion filters
        query = query.filter(
            or_(
                Activity.task_id.is_(None),
                Activity.task_id.in_(task_subquery)
            )
        ).filter(
            or_(
                Activity.shot_id.is_(None),
                Activity.shot_id.in_(shot_subquery)
            )
        ).filter(
            or_(
                Activity.asset_id.is_(None),
                Activity.asset_id.in_(asset_subquery)
            )
        ).filter(
            or_(
                Activity.submission_id.is_(None),
                Activity.submission_id.in_(submission_subquery)
            )
        )
        
        return query.order_by(desc(Activity.created_at)).offset(skip).limit(limit).all()
    
    @staticmethod
    def get_activities_including_deleted(
        db: Session,
        project_id: Optional[int] = None,
        task_id: Optional[int] = None,
        user_id: Optional[int] = None,
        skip: int = 0,
        limit: int = 50,
        type_filter: Optional[ActivityType] = None,
        days: Optional[int] = None
    ):
        """Get all activities including those related to deleted records (admin only)."""
        from sqlalchemy import desc
        from datetime import datetime, timedelta
        
        query = db.query(Activity)
        
        # Base filters
        if project_id:
            query = query.filter(Activity.project_id == project_id)
        if task_id:
            query = query.filter(Activity.task_id == task_id)
        if user_id:
            query = query.filter(Activity.user_id == user_id)
        if type_filter:
            query = query.filter(Activity.type == type_filter)
        if days:
            cutoff_date = datetime.utcnow() - timedelta(days=days)
            query = query.filter(Activity.created_at >= cutoff_date)
        
        return query.order_by(desc(Activity.created_at)).offset(skip).limit(limit).all()


# Global activity service instance
activity_service = ActivityService()
