from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.orm import Session, joinedload
from sqlalchemy import and_
from typing import List, Optional

from database import get_db
from models.task import Task, Submission, Review, TaskStatus
from models.user import User, UserRole
from schemas.task import ReviewCreate, ReviewResponse, SubmissionResponse
from utils.auth import get_current_user_from_token, _get_user_from_db
from utils.notifications import notification_service

router = APIRouter()


def get_current_user(
    token_data: dict = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """Get current user with proper database dependency."""
    return _get_user_from_db(db, token_data["user_id"])


def require_director_coordinator_or_admin(
    token_data: dict = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """Dependency to require director, coordinator role, or admin permission."""
    current_user = _get_user_from_db(db, token_data["user_id"])
    if (current_user.role not in [UserRole.DIRECTOR, UserRole.COORDINATOR] and 
        not current_user.is_admin):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Director role, Coordinator role, or Admin permission required"
        )
    return current_user


def require_role(required_roles: list):
    """Create a dependency that requires specific user roles."""
    def role_checker(
        token_data: dict = Depends(get_current_user_from_token),
        db: Session = Depends(get_db)
    ):
        current_user = _get_user_from_db(db, token_data["user_id"])
        if current_user.role not in required_roles:
            raise HTTPException(
                status_code=403,
                detail="Insufficient permissions"
            )
        return current_user
    return role_checker


@router.get("/pending", response_model=List[SubmissionResponse])
async def get_pending_reviews(
    project_id: Optional[int] = Query(None, description="Filter by project ID"),
    skip: int = Query(0, ge=0),
    limit: int = Query(100, ge=1, le=1000),
    db: Session = Depends(get_db),
    current_user: User = Depends(require_director_coordinator_or_admin)
):
    """Get all submissions pending review. Only directors, coordinators, and users with admin permission can access."""
    
    # Get submissions that don't have any reviews yet or have retake reviews
    query = db.query(Submission).options(
        joinedload(Submission.user),
        joinedload(Submission.task).joinedload(Task.project),
        joinedload(Submission.reviews).joinedload("reviewer")
    ).join(Task).filter(
        Submission.deleted_at.is_(None),
        Task.deleted_at.is_(None)
    )
    
    # Filter by project if specified
    if project_id:
        query = query.filter(Task.project_id == project_id)
    
    # Only get submitted tasks
    query = query.filter(Task.status == "submitted")
    
    submissions = query.order_by(Submission.submitted_at.desc()).offset(skip).limit(limit).all()
    
    # Filter to only include submissions that need review
    pending_submissions = []
    for submission in submissions:
        # Check if submission has any approved reviews
        has_approved_review = any(review.decision == "approved" for review in submission.reviews)
        
        if not has_approved_review:
            # Get latest review
            latest_review = None
            if submission.reviews:
                latest_review_obj = max(submission.reviews, key=lambda r: r.reviewed_at)
                latest_review = {
                    "id": latest_review_obj.id,
                    "submission_id": latest_review_obj.submission_id,
                    "reviewer_id": latest_review_obj.reviewer_id,
                    "decision": latest_review_obj.decision,
                    "feedback": latest_review_obj.feedback,
                    "reviewed_at": latest_review_obj.reviewed_at,
                    "reviewer_first_name": latest_review_obj.reviewer.first_name,
                    "reviewer_last_name": latest_review_obj.reviewer.last_name
                }
            
            submission_data = {
                "id": submission.id,
                "task_id": submission.task_id,
                "user_id": submission.user_id,
                "file_path": submission.file_path,
                "file_name": submission.file_name,
                "version_number": submission.version_number,
                "notes": submission.notes,
                "submitted_at": submission.submitted_at,
                "user_first_name": submission.user.first_name,
                "user_last_name": submission.user.last_name,
                "latest_review": latest_review
            }
            pending_submissions.append(SubmissionResponse(**submission_data))
    
    return pending_submissions


@router.post("/{submission_id}/approve", response_model=ReviewResponse)
async def approve_submission(
    submission_id: int,
    review: ReviewCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_director_coordinator_or_admin)
):
    """Approve a submission. Only directors, coordinators, and users with admin permission can approve."""
    
    submission = db.query(Submission).options(
        joinedload(Submission.task)
    ).filter(
        Submission.id == submission_id,
        Submission.deleted_at.is_(None)
    ).first()
    
    if not submission:
        raise HTTPException(status_code=404, detail="Submission not found")
    
    # Check if submission is in submitted state
    if submission.task.status != "submitted":
        raise HTTPException(status_code=400, detail="Submission is not in submitted state")
    
    # Force decision to approved
    review.decision = "approved"
    
    # Create review record
    db_review = Review(
        submission_id=submission_id,
        reviewer_id=current_user.id,
        decision=review.decision,
        feedback=review.feedback
    )
    db.add(db_review)
    
    # Update task status to approved
    submission.task.status = "approved"
    
    db.commit()
    db.refresh(db_review)
    
    # Send notification to artist
    notification_service.notify_submission_reviewed(db, submission, db_review, current_user)
    
    # Load reviewer information for response
    db_review = db.query(Review).options(
        joinedload(Review.reviewer)
    ).filter(Review.id == db_review.id).first()
    
    review_data = {
        "id": db_review.id,
        "submission_id": db_review.submission_id,
        "reviewer_id": db_review.reviewer_id,
        "decision": db_review.decision,
        "feedback": db_review.feedback,
        "reviewed_at": db_review.reviewed_at,
        "reviewer_first_name": db_review.reviewer.first_name,
        "reviewer_last_name": db_review.reviewer.last_name
    }
    
    return ReviewResponse(**review_data)


@router.post("/{submission_id}/retake", response_model=ReviewResponse)
async def request_retake(
    submission_id: int,
    review: ReviewCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_director_coordinator_or_admin)
):
    """Request a retake for a submission. Only directors, coordinators, and users with admin permission can request retakes."""
    
    submission = db.query(Submission).options(
        joinedload(Submission.task)
    ).filter(
        Submission.id == submission_id,
        Submission.deleted_at.is_(None)
    ).first()
    
    if not submission:
        raise HTTPException(status_code=404, detail="Submission not found")
    
    # Check if submission is in submitted state
    if submission.task.status != "submitted":
        raise HTTPException(status_code=400, detail="Submission is not in submitted state")
    
    # Force decision to retake and require feedback
    review.decision = "retake"
    if not review.feedback or review.feedback.strip() == "":
        raise HTTPException(status_code=400, detail="Feedback is required when requesting a retake")
    
    # Create review record
    db_review = Review(
        submission_id=submission_id,
        reviewer_id=current_user.id,
        decision=review.decision,
        feedback=review.feedback
    )
    db.add(db_review)
    
    # Update task status to retake
    submission.task.status = "retake"
    
    db.commit()
    db.refresh(db_review)
    
    # Send notification to artist
    notification_service.notify_submission_reviewed(db, submission, db_review, current_user)
    
    # Load reviewer information for response
    db_review = db.query(Review).options(
        joinedload(Review.reviewer)
    ).filter(Review.id == db_review.id).first()
    
    review_data = {
        "id": db_review.id,
        "submission_id": db_review.submission_id,
        "reviewer_id": db_review.reviewer_id,
        "decision": db_review.decision,
        "feedback": db_review.feedback,
        "reviewed_at": db_review.reviewed_at,
        "reviewer_first_name": db_review.reviewer.first_name,
        "reviewer_last_name": db_review.reviewer.last_name
    }
    
    return ReviewResponse(**review_data)


@router.get("/{submission_id}/reviews", response_model=List[ReviewResponse])
async def get_submission_reviews(
    submission_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """Get all reviews for a submission."""
    
    submission = db.query(Submission).options(
        joinedload(Submission.task)
    ).filter(
        Submission.id == submission_id,
        Submission.deleted_at.is_(None)
    ).first()
    
    if not submission:
        raise HTTPException(status_code=404, detail="Submission not found")
    
    # Artists can only view reviews for their own submissions
    if current_user.role == UserRole.ARTIST and submission.user_id != current_user.id:
        raise HTTPException(status_code=403, detail="Not authorized to view reviews for this submission")
    
    reviews = db.query(Review).options(
        joinedload(Review.reviewer)
    ).filter(
        Review.submission_id == submission_id,
        Review.deleted_at.is_(None)
    ).order_by(Review.reviewed_at.desc()).all()
    
    result = []
    for review in reviews:
        review_data = {
            "id": review.id,
            "submission_id": review.submission_id,
            "reviewer_id": review.reviewer_id,
            "decision": review.decision,
            "feedback": review.feedback,
            "reviewed_at": review.reviewed_at,
            "reviewer_first_name": review.reviewer.first_name,
            "reviewer_last_name": review.reviewer.last_name
        }
        result.append(ReviewResponse(**review_data))
    
    return result