from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File
from sqlalchemy.orm import Session
from typing import List, Optional
from passlib.context import CryptContext
from pathlib import Path
from PIL import Image
import os
import hashlib
from datetime import datetime

from database import get_db
from models.user import User, UserRole
from models.project import ProjectMember
from models.task import Task
from schemas.user import UserResponse, UserApproval, UserRoleUpdate, UserUpdate, UserAdminUpdate, UserAdminCreate, UserAdminEdit, UserPasswordReset, UserPasswordChange
from utils.auth import get_current_user_from_token, _get_user_from_db, require_admin_permission

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

router = APIRouter()


def require_admin_permission_with_db(
    token_data: dict = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """Dependency to require admin permission."""
    current_user = _get_user_from_db(db, token_data["user_id"])
    if not current_user.is_admin:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Admin permission required"
        )
    return current_user


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


@router.post("/{user_id}/approve", response_model=dict)
async def approve_user(
    user_id: int,
    approval_data: UserApproval,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission_with_db)
):
    """Approve or disapprove a user account (Admin permission required)."""
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    user.is_approved = approval_data.is_approved
    db.commit()
    
    action = "approved" if approval_data.is_approved else "disapproved"
    return {
        "message": f"User {user.email} has been {action}",
        "user_id": user.id,
        "is_approved": user.is_approved
    }


@router.put("/{user_id}/role", response_model=dict)
async def update_user_role(
    user_id: int,
    role_data: UserRoleUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission_with_db)
):
    """Update user role (Admin permission required)."""
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    # Prevent admin from changing their own role
    if user.id == current_user.id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Cannot change your own role"
        )
    
    user.role = role_data.role
    db.commit()
    
    return {
        "message": f"User {user.email} role updated to {role_data.role}",
        "user_id": user.id,
        "role": user.role
    }


@router.put("/{user_id}/admin", response_model=dict)
async def update_user_admin_permission(
    user_id: int,
    admin_data: UserAdminUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission_with_db)
):
    """Grant or revoke admin permission (Admin permission required)."""
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    # Prevent user from revoking their own admin permission if they're the only admin
    if user.id == current_user.id and not admin_data.is_admin:
        admin_count = db.query(User).filter(User.is_admin == True).count()
        if admin_count <= 1:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Cannot revoke admin permission - you are the only admin user"
            )
    
    user.is_admin = admin_data.is_admin
    db.commit()
    
    action = "granted" if admin_data.is_admin else "revoked"
    return {
        "message": f"Admin permission {action} for user {user.email}",
        "user_id": user.id,
        "is_admin": user.is_admin
    }


@router.get("/", response_model=List[UserResponse])
async def list_users(
    skip: int = 0,
    limit: int = 100,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_or_coordinator)
):
    """List all users (Admin and Coordinator only)."""
    users = db.query(User).offset(skip).limit(limit).all()
    return users


@router.get("/pending", response_model=List[UserResponse])
async def list_pending_users(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission_with_db)
):
    """List users pending approval (Admin permission required)."""
    pending_users = db.query(User).filter(User.is_approved == False).all()
    return pending_users


@router.get("/me", response_model=UserResponse)
async def get_current_user_profile(
    token_data: dict = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """Get current user's profile."""
    current_user = _get_user_from_db(db, token_data["user_id"])
    return current_user


@router.put("/me", response_model=UserResponse)
async def update_current_user_profile(
    user_update: UserUpdate,
    token_data: dict = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """Update current user's profile."""
    current_user = _get_user_from_db(db, token_data["user_id"])
    
    # Users can only update their own name, not role or approval status
    if user_update.first_name is not None:
        current_user.first_name = user_update.first_name
    if user_update.last_name is not None:
        current_user.last_name = user_update.last_name
    
    # Only users with admin permission can update role and approval status
    if user_update.role is not None or user_update.is_approved is not None or user_update.is_admin is not None:
        if not current_user.is_admin:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Admin permission required to update role, approval status, or admin permission"
            )
        if user_update.role is not None:
            current_user.role = user_update.role
        if user_update.is_approved is not None:
            current_user.is_approved = user_update.is_approved
        if user_update.is_admin is not None:
            current_user.is_admin = user_update.is_admin
    
    db.commit()
    db.refresh(current_user)
    return current_user


@router.get("/{user_id}", response_model=UserResponse)
async def get_user(
    user_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_or_coordinator)
):
    """Get user by ID (Admin and Coordinator only)."""
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    return user


@router.post("/admin/create", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def admin_create_user(
    user_data: UserAdminCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission_with_db)
):
    """Create a new user account (Admin permission required)."""
    # Check if email already exists
    existing_user = db.query(User).filter(User.email == user_data.email).first()
    if existing_user:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Email already registered"
        )
    
    # Hash the password
    hashed_password = pwd_context.hash(user_data.password)
    
    # Create new user
    new_user = User(
        email=user_data.email,
        password_hash=hashed_password,
        first_name=user_data.first_name,
        last_name=user_data.last_name,
        role=user_data.role,
        is_approved=user_data.is_approved,
        is_admin=user_data.is_admin
    )
    
    db.add(new_user)
    db.commit()
    db.refresh(new_user)
    
    return new_user


@router.put("/{user_id}", response_model=UserResponse)
async def admin_edit_user(
    user_id: int,
    user_data: UserAdminEdit,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission_with_db)
):
    """Edit user account (Admin permission required)."""
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    # Check if email is being changed and if it's already in use
    if user_data.email and user_data.email != user.email:
        existing_user = db.query(User).filter(User.email == user_data.email).first()
        if existing_user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Email already in use"
            )
        user.email = user_data.email
    
    # Prevent admin from removing their own admin permission
    if user_data.is_admin is not None and user.id == current_user.id and not user_data.is_admin:
        admin_count = db.query(User).filter(User.is_admin == True).count()
        if admin_count <= 1:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Cannot remove your own admin permission - you are the only admin user"
            )
    
    # Update fields
    if user_data.first_name is not None:
        user.first_name = user_data.first_name
    if user_data.last_name is not None:
        user.last_name = user_data.last_name
    if user_data.role is not None:
        user.role = user_data.role
    if user_data.is_approved is not None:
        user.is_approved = user_data.is_approved
    if user_data.is_admin is not None:
        user.is_admin = user_data.is_admin
    
    db.commit()
    db.refresh(user)
    
    return user


@router.put("/{user_id}/password", response_model=dict)
async def admin_reset_user_password(
    user_id: int,
    password_data: UserPasswordReset,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission_with_db)
):
    """Reset user password (Admin permission required)."""
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    # Hash the new password
    hashed_password = pwd_context.hash(password_data.new_password)
    user.password_hash = hashed_password
    
    db.commit()
    
    return {
        "message": f"Password reset successfully for user {user.email}",
        "user_id": user.id
    }


@router.get("/{user_id}/can-delete", response_model=dict)
async def check_user_can_delete(
    user_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission_with_db)
):
    """Check if a user can be deleted (Admin permission required)."""
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    # Check if trying to delete themselves
    if user.id == current_user.id:
        return {
            "can_delete": False,
            "reason": "Cannot delete your own account",
            "project_memberships": 0,
            "task_assignments": 0
        }
    
    # Check for project memberships
    project_memberships = db.query(ProjectMember).filter(ProjectMember.user_id == user_id).count()
    
    # Check for task assignments
    task_assignments = db.query(Task).filter(Task.assigned_user_id == user_id).count()
    
    can_delete = project_memberships == 0 and task_assignments == 0
    reason = None
    
    if not can_delete:
        reasons = []
        if project_memberships > 0:
            reasons.append(f"{project_memberships} project membership(s)")
        if task_assignments > 0:
            reasons.append(f"{task_assignments} task assignment(s)")
        reason = f"User has {' and '.join(reasons)}. Please remove user from projects and reassign tasks first."
    
    return {
        "can_delete": can_delete,
        "reason": reason,
        "project_memberships": project_memberships,
        "task_assignments": task_assignments
    }


@router.delete("/{user_id}", response_model=dict)
async def admin_delete_user(
    user_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_admin_permission_with_db)
):
    """Delete user account (Admin permission required)."""
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    
    # Prevent admin from deleting themselves
    if user.id == current_user.id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Cannot delete your own account"
        )
    
    # Check for project memberships
    project_memberships = db.query(ProjectMember).filter(ProjectMember.user_id == user_id).count()
    if project_memberships > 0:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Cannot delete user - user has {project_memberships} project membership(s). Please remove user from projects first."
        )
    
    # Check for task assignments
    task_assignments = db.query(Task).filter(Task.assigned_user_id == user_id).count()
    if task_assignments > 0:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Cannot delete user - user has {task_assignments} task assignment(s). Please reassign tasks first."
        )
    
    # Delete the user
    db.delete(user)
    db.commit()
    
    return {
        "message": f"User {user.email} deleted successfully",
        "user_id": user_id
    }


@router.post("/me/avatar", response_model=UserResponse)
async def upload_avatar(
    file: UploadFile = File(...),
    token_data: dict = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """Upload user avatar image."""
    from utils.file_handler import file_handler
    
    current_user = _get_user_from_db(db, token_data["user_id"])
    
    # Validate file format
    allowed_formats = {'.jpg', '.jpeg', '.png', '.gif', '.webp'}
    file_extension = Path(file.filename).suffix.lower()
    
    if file_extension not in allowed_formats:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Invalid file format. Allowed formats: {', '.join(allowed_formats)}"
        )
    
    # Read file content
    file_content = await file.read()
    file_size = len(file_content)
    
    # Validate file size (5MB max)
    max_size = 5 * 1024 * 1024  # 5MB
    if file_size > max_size:
        raise HTTPException(
            status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
            detail="File too large. Maximum size is 5MB"
        )
    
    # Create avatars directory using FileHandler's base structure
    avatars_dir = file_handler.base_upload_dir / "avatars"
    avatars_dir.mkdir(parents=True, exist_ok=True)
    
    # Generate unique filename
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    hash_input = f"{current_user.id}{timestamp}".encode()
    short_hash = hashlib.md5(hash_input).hexdigest()[:8]
    unique_filename = f"user_{current_user.id}_{timestamp}_{short_hash}{file_extension}"
    file_path = avatars_dir / unique_filename
    
    # Save temporary file
    temp_path = avatars_dir / f"temp_{unique_filename}"
    with open(temp_path, "wb") as buffer:
        buffer.write(file_content)
    
    try:
        # Process image: resize and crop to 200x200
        with Image.open(temp_path) as img:
            # Convert to RGB if necessary
            if img.mode in ('RGBA', 'LA', 'P'):
                # For RGBA, create white background
                if img.mode == 'RGBA':
                    background = Image.new('RGB', img.size, (255, 255, 255))
                    background.paste(img, mask=img.split()[3])  # Use alpha channel as mask
                    img = background
                else:
                    img = img.convert('RGB')
            
            # Resize to 200x200 (crop to square first)
            width, height = img.size
            min_dimension = min(width, height)
            
            # Crop to square from center
            left = (width - min_dimension) // 2
            top = (height - min_dimension) // 2
            right = left + min_dimension
            bottom = top + min_dimension
            img = img.crop((left, top, right, bottom))
            
            # Resize to 200x200
            img = img.resize((200, 200), Image.Resampling.LANCZOS)
            
            # Save processed image
            img.save(file_path, 'JPEG', quality=90)
        
        # Delete temporary file
        os.remove(temp_path)
        
        # Delete old avatar if exists
        if current_user.avatar_url:
            # Resolve old avatar path and delete
            old_avatar_absolute_path = file_handler.resolve_absolute_path(current_user.avatar_url)
            if os.path.exists(old_avatar_absolute_path):
                try:
                    os.remove(old_avatar_absolute_path)
                except Exception:
                    pass  # Ignore errors deleting old avatar
        
        # Store relative path in database using FileHandler
        relative_avatar_path = file_handler.store_relative_path(str(file_path))
        current_user.avatar_url = relative_avatar_path
        db.commit()
        db.refresh(current_user)
        
        return current_user
        
    except Exception as e:
        # Clean up temporary file on error
        if temp_path.exists():
            os.remove(temp_path)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to process image: {str(e)}"
        )


@router.delete("/me/avatar", response_model=UserResponse)
async def remove_avatar(
    token_data: dict = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """Remove user avatar image."""
    from utils.file_handler import file_handler
    
    current_user = _get_user_from_db(db, token_data["user_id"])
    
    # Delete avatar file if exists
    if current_user.avatar_url:
        # Resolve to absolute path for file deletion
        avatar_absolute_path = file_handler.resolve_absolute_path(current_user.avatar_url)
        if os.path.exists(avatar_absolute_path):
            try:
                os.remove(avatar_absolute_path)
            except Exception:
                pass  # Ignore errors deleting avatar file
    
    # Clear avatar URL
    current_user.avatar_url = None
    db.commit()
    db.refresh(current_user)
    
    return current_user


@router.put("/me/password", response_model=dict)
async def change_password(
    password_data: UserPasswordChange,
    token_data: dict = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """Change current user's password."""
    current_user = _get_user_from_db(db, token_data["user_id"])
    
    # Verify current password
    if not pwd_context.verify(password_data.current_password, current_user.password_hash):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Incorrect current password"
        )
    
    # Validate new password (basic validation)
    if len(password_data.new_password) < 8:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="New password must be at least 8 characters long"
        )
    
    # Hash and update password
    current_user.password_hash = pwd_context.hash(password_data.new_password)
    db.commit()
    
    return {
        "message": "Password changed successfully",
        "user_id": current_user.id
    }