from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List, Dict

from database import get_db
from models.asset import Asset, AssetCategory
from models.project import Project, ProjectMember
from models.task import Task, TaskType, TaskStatus
from models.user import User, UserRole
from schemas.asset import AssetCreate, AssetUpdate, AssetResponse, AssetListResponse, TaskStatusInfo
from schemas.task import TaskCreate
from utils.auth import get_current_user_from_token
from services.asset_soft_deletion import AssetSoftDeletionService

router = APIRouter()


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


def require_coordinator_or_admin(
    token_data: dict = Depends(get_current_user_from_token),
    db: Session = Depends(get_db)
):
    """Require coordinator or admin role."""
    from utils.auth import _get_user_from_db
    current_user = _get_user_from_db(db, token_data["user_id"])
    
    if current_user.role != UserRole.COORDINATOR and not current_user.is_admin:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Insufficient permissions"
        )
    return current_user


def get_status_sort_order(status: str, project_custom_statuses: list = None) -> int:
    """Get sort order for task status, including custom statuses."""
    # Default system status order
    system_status_order = {
        "not_started": 0,
        "in_progress": 1,
        "submitted": 2,
        "retake": 3,
        "approved": 4
    }
    
    # If it's a system status, return its order
    if status in system_status_order:
        return system_status_order[status]
    
    # For custom statuses, use their defined order + offset to place them after system statuses
    if project_custom_statuses:
        for custom_status in project_custom_statuses:
            if isinstance(custom_status, dict) and custom_status.get('id') == status:
                # Custom statuses start after system statuses (5+)
                return 5 + custom_status.get('order', 0)
    
    # Unknown status defaults to 0 (same as not_started)
    return 0


def get_project_custom_statuses(project_id: int, db: Session) -> list:
    """Get custom task statuses for a project."""
    project = db.query(Project).filter(Project.id == project_id).first()
    if not project or not project.custom_task_statuses:
        return []
    
    custom_statuses_data = project.custom_task_statuses
    if isinstance(custom_statuses_data, str):
        try:
            import json
            custom_statuses_data = json.loads(custom_statuses_data)
        except (json.JSONDecodeError, TypeError):
            return []
    
    return custom_statuses_data if isinstance(custom_statuses_data, list) else []


def check_project_access(project_id: int, current_user: User, db: Session):
    """Check if user has access to the project."""
    # Check if project exists
    project = db.query(Project).filter(Project.id == project_id).first()
    if not project:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Project not found"
        )
    
    # Check access for artists
    if current_user.role == UserRole.ARTIST:
        member = db.query(ProjectMember).filter(
            ProjectMember.project_id == project_id,
            ProjectMember.user_id == current_user.id
        ).first()
        if not member:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Access denied to this project"
            )
    
    return project


# Standard asset task types (read-only)
STANDARD_ASSET_TASK_TYPES = ["modeling", "surfacing", "rigging"]

# Default asset tasks by category (using string values instead of enums)
DEFAULT_ASSET_TASKS = {
    AssetCategory.CHARACTERS: [TaskType.MODELING.value, TaskType.SURFACING.value, TaskType.RIGGING.value],
    AssetCategory.PROPS: [TaskType.MODELING.value, TaskType.SURFACING.value],
    AssetCategory.SETS: [TaskType.MODELING.value, TaskType.SURFACING.value],
    AssetCategory.VEHICLES: [TaskType.MODELING.value, TaskType.SURFACING.value, TaskType.RIGGING.value]
}


def get_default_asset_task_types(category: AssetCategory) -> List[str]:
    """Get default task types for an asset category."""
    return DEFAULT_ASSET_TASKS.get(category, [])


def get_all_asset_task_types(project_id: int, db: Session) -> List[str]:
    """Get all task types (standard + custom) for assets in a project."""
    project = db.query(Project).filter(Project.id == project_id).first()
    if not project:
        return STANDARD_ASSET_TASK_TYPES
    
    custom_types = project.custom_asset_task_types or []
    return STANDARD_ASSET_TASK_TYPES + custom_types


def create_default_tasks_for_asset(asset: Asset, task_types: List[str], db: Session) -> List[Task]:
    """Create default tasks for an asset."""
    created_tasks = []
    
    for task_type in task_types:
        # Create task name based on type
        task_name = f"{asset.name} - {task_type.title()}"
        
        # Create the task
        db_task = Task(
            project_id=asset.project_id,
            asset_id=asset.id,
            task_type=task_type,
            name=task_name,
            description=f"Default {task_type} task for {asset.name}",
            status="not_started"
        )
        
        db.add(db_task)
        created_tasks.append(db_task)
    
    return created_tasks


@router.get("/categories", response_model=List[str])
async def list_asset_categories():
    """List all available asset categories"""
    return [category.value for category in AssetCategory]


@router.get("/default-tasks/{category}", response_model=List[str])
async def get_default_tasks_for_category(
    category: AssetCategory,
    project_id: int = None,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user_with_db)
):
    """Get default task types for an asset category (includes custom types if project_id provided)"""
    task_types = get_default_asset_task_types(category)
    
    # If project_id is provided, include custom task types
    if project_id:
        all_types = get_all_asset_task_types(project_id, db)
        # Return only the types that are relevant for this category
        # For now, return all available types (standard + custom)
        return all_types
    
    return task_types  # task_types are already strings, no need for .value


@router.get("/", response_model=List[AssetListResponse])
async def list_assets(
    project_id: int = None,
    category: AssetCategory = None,
    task_status_filter: str = None,
    sort_by: str = None,
    sort_direction: str = "asc",
    skip: int = 0,
    limit: int = 100,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user_with_db)
):
    """List assets with optional filtering by project and category"""
    from sqlalchemy.orm import joinedload, selectinload
    
    # Build base query for assets (exclude soft deleted)
    base_query = db.query(Asset).filter(Asset.deleted_at.is_(None))
    
    # Filter by project if specified
    if project_id:
        check_project_access(project_id, current_user, db)
        base_query = base_query.filter(Asset.project_id == project_id)
    else:
        # If no project specified, filter by user's accessible projects for artists
        if current_user.role == UserRole.ARTIST:
            accessible_projects = db.query(ProjectMember.project_id).filter(
                ProjectMember.user_id == current_user.id
            ).subquery()
            base_query = base_query.filter(Asset.project_id.in_(accessible_projects))
    
    # Filter by category if specified
    if category:
        base_query = base_query.filter(Asset.category == category)
    
    # Apply sorting if specified (for non-task-status fields)
    if sort_by and not sort_by.endswith('_status'):
        if sort_by in ['name', 'category', 'status', 'created_at', 'updated_at']:
            sort_column = getattr(Asset, sort_by)
            if sort_direction.lower() == 'desc':
                base_query = base_query.order_by(sort_column.desc())
            else:
                base_query = base_query.order_by(sort_column.asc())
    
    # OPTIMIZATION: Use single query with optimized JOIN to fetch assets and their tasks
    # This replaces the N+1 query pattern with a single database operation
    assets_with_tasks = (
        base_query
        .outerjoin(Task, (Task.asset_id == Asset.id) & (Task.deleted_at.is_(None)))
        .options(
            joinedload(Asset.project),  # Eager load project
            selectinload(Asset.tasks).options(  # Use selectinload for better performance with tasks
                selectinload(Task.assigned_user)  # Eager load assigned users
            )
        )
        .add_columns(
            Task.id.label('task_id'),
            Task.task_type,
            Task.status.label('task_status'),
            Task.assigned_user_id,
            Task.updated_at.label('task_updated_at')  # Include task update time for better tracking
        )
        .offset(skip)
        .limit(limit)
        .all()
    )
    
    # OPTIMIZATION: Pre-fetch all project data and task types in a single query
    # This eliminates the need for repeated project queries
    project_ids = set()
    for row in assets_with_tasks:
        asset = row[0]
        if asset.project_id not in project_ids:
            project_ids.add(asset.project_id)
    
    # Get all projects with their custom task types in one optimized query
    project_data = {}
    if project_ids:
        projects = (
            db.query(Project)
            .filter(Project.id.in_(project_ids))
            .all()
        )
        for project in projects:
            custom_types = project.custom_asset_task_types or []
            project_data[project.id] = {
                'task_types': STANDARD_ASSET_TASK_TYPES + custom_types,
                'custom_statuses': get_project_custom_statuses(project.id, db)
            }
    
    # OPTIMIZATION: Group results by asset and aggregate task data efficiently
    assets_dict = {}
    for row in assets_with_tasks:
        asset = row[0]  # Asset object
        task_id = row[1]  # task_id
        task_type = row[2]  # task_type
        task_status = row[3]  # task_status
        assigned_user_id = row[4]  # assigned_user_id
        task_updated_at = row[5]  # task_updated_at
        
        if asset.id not in assets_dict:
            # Initialize asset data with pre-fetched project data
            project_info = project_data.get(asset.project_id, {
                'task_types': STANDARD_ASSET_TASK_TYPES,
                'custom_statuses': []
            })
            
            assets_dict[asset.id] = {
                'asset': asset,
                'tasks': [],
                'task_status': {},
                'task_details': [],
                'project_info': project_info
            }
            
            # Initialize all task types as not started using pre-fetched data
            for task_type_init in project_info['task_types']:
                assets_dict[asset.id]['task_status'][task_type_init] = "not_started"
        
        # Add task data if task exists
        if task_id is not None:
            assets_dict[asset.id]['tasks'].append({
                'task_id': task_id,
                'task_type': task_type,
                'status': task_status,
                'assigned_user_id': assigned_user_id,
                'updated_at': task_updated_at
            })
            
            # Update task status
            assets_dict[asset.id]['task_status'][task_type] = task_status
            
            # Add to task details with enhanced information
            assets_dict[asset.id]['task_details'].append(TaskStatusInfo(
                task_type=task_type,
                status=task_status,
                task_id=task_id,
                assigned_user_id=assigned_user_id
            ))
    
    # Build response list efficiently
    result = []
    for asset_data in assets_dict.values():
        asset = asset_data['asset']
        
        # Create asset response with optimized data
        asset_response = AssetListResponse.model_validate(asset)
        asset_response.task_count = len(asset_data['tasks'])
        asset_response.task_status = asset_data['task_status']
        asset_response.task_details = asset_data['task_details']
        
        result.append(asset_response)
    
    # Apply task status filtering if specified
    if task_status_filter:
        try:
            # Parse task status filter (format: "task_type:status")
            task_type, status = task_status_filter.split(":")
            filter_status = status  # Use string directly instead of enum
            result = [
                asset for asset in result 
                if asset.task_status.get(task_type) == filter_status
            ]
        except (ValueError, KeyError):
            # Invalid filter format, ignore
            pass
    
    # Apply task status sorting if specified
    if sort_by and sort_by.endswith('_status'):
        task_type = sort_by.replace('_status', '')
        
        # Get custom statuses for proper sorting using pre-fetched data
        def get_status_order(asset):
            status = asset.task_status.get(task_type, "not_started")
            # Use pre-fetched custom statuses from project_data
            asset_project_data = project_data.get(getattr(asset, 'project_id', None), {})
            custom_statuses = asset_project_data.get('custom_statuses', [])
            return get_status_sort_order(status, custom_statuses)
        
        reverse = sort_direction.lower() == 'desc'
        result.sort(key=get_status_order, reverse=reverse)
    
    return result


@router.post("/", response_model=AssetResponse, status_code=status.HTTP_201_CREATED)
async def create_asset(
    asset: AssetCreate,
    project_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_coordinator_or_admin)
):
    """Create a new asset in a project with optional default tasks"""
    # Check project access
    check_project_access(project_id, current_user, db)
    
    # Check if asset name already exists in project (exclude soft deleted)
    existing_asset = db.query(Asset).filter(
        Asset.project_id == project_id,
        Asset.name == asset.name,
        Asset.deleted_at.is_(None)
    ).first()
    
    if existing_asset:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Asset with this name already exists in the project"
        )
    
    # Create new asset (exclude fields that don't belong to Asset model)
    asset_data = asset.model_dump(exclude={'create_default_tasks', 'selected_task_types'})
    db_asset = Asset(
        project_id=project_id,
        **asset_data
    )
    
    db.add(db_asset)
    db.flush()  # Flush to get the asset ID
    
    # Create default tasks if requested
    task_count = 0
    if asset.create_default_tasks:
        # Determine which task types to create
        if asset.selected_task_types:
            # Use the selected task types (already strings, can include custom types)
            task_types = asset.selected_task_types
            
            # Validate that all selected task types are valid (standard or custom)
            all_valid_types = get_all_asset_task_types(project_id, db)
            invalid_types = [t for t in task_types if t not in all_valid_types]
            if invalid_types:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"Invalid task types: {', '.join(invalid_types)}"
                )
        else:
            # Use default task types for the asset category
            task_types = get_default_asset_task_types(asset.category)
        
        # Create the tasks
        created_tasks = create_default_tasks_for_asset(db_asset, task_types, db)
        task_count = len(created_tasks)
    
    db.commit()
    db.refresh(db_asset)
    
    # Add task count
    asset_data = AssetResponse.model_validate(db_asset)
    asset_data.task_count = task_count
    
    return asset_data


@router.get("/{asset_id}", response_model=AssetResponse)
async def get_asset(
    asset_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user_with_db)
):
    """Get a specific asset by ID"""
    from sqlalchemy.orm import joinedload, selectinload
    
    # OPTIMIZATION: Use single query with optimized JOINs to fetch asset and all related data
    # This replaces separate queries with a single database operation
    asset_query = (
        db.query(Asset)
        .options(
            joinedload(Asset.project),  # Eager load project
            selectinload(Asset.tasks).options(  # Use selectinload for better performance with tasks
                selectinload(Task.assigned_user)  # Eager load assigned users if needed
            )
        )
        .filter(Asset.id == asset_id, Asset.deleted_at.is_(None))
    )
    
    asset = asset_query.first()
    
    if not asset:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Asset not found"
        )
    
    # Check project access
    check_project_access(asset.project_id, current_user, db)
    
    # OPTIMIZATION: Count tasks from the already loaded relationship
    # This avoids a separate COUNT query
    active_tasks = [task for task in asset.tasks if task.deleted_at is None]
    task_count = len(active_tasks)
    
    asset_data = AssetResponse.model_validate(asset)
    asset_data.task_count = task_count
    
    return asset_data


@router.get("/{asset_id}/task-status", response_model=List[TaskStatusInfo])
async def get_asset_task_status(
    asset_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user_with_db)
):
    """Get detailed task status for a specific asset"""
    # Exclude soft deleted assets
    asset = db.query(Asset).filter(
        Asset.id == asset_id,
        Asset.deleted_at.is_(None)
    ).first()
    
    if not asset:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Asset not found"
        )
    
    # Check project access
    check_project_access(asset.project_id, current_user, db)
    
    # Get all active tasks for this asset (exclude soft deleted)
    tasks = db.query(Task).filter(
        Task.asset_id == asset.id,
        Task.deleted_at.is_(None)
    ).all()
    
    # Build detailed task status information
    task_details = []
    for task in tasks:
        task_details.append(TaskStatusInfo(
            task_type=task.task_type,
            status=task.status,
            task_id=task.id,
            assigned_user_id=task.assigned_user_id
        ))
    
    return task_details


@router.post("/{asset_id}/tasks", response_model=TaskStatusInfo, status_code=status.HTTP_201_CREATED)
async def create_asset_task(
    asset_id: int,
    task_type: str,  # Changed from TaskType enum to str
    db: Session = Depends(get_db),
    current_user: User = Depends(require_coordinator_or_admin)
):
    """Create a new task for an asset"""
    # Exclude soft deleted assets
    asset = db.query(Asset).filter(
        Asset.id == asset_id,
        Asset.deleted_at.is_(None)
    ).first()
    
    if not asset:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Asset not found"
        )
    
    # Check project access
    check_project_access(asset.project_id, current_user, db)
    
    # Check if task already exists (exclude soft deleted)
    existing_task = db.query(Task).filter(
        Task.asset_id == asset_id,
        Task.task_type == task_type,
        Task.deleted_at.is_(None)
    ).first()
    
    if existing_task:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Task already exists for this asset and task type"
        )
    
    # Create the task
    task_name = f"{asset.name} - {task_type.title()}"
    db_task = Task(
        project_id=asset.project_id,
        asset_id=asset.id,
        task_type=task_type,
        name=task_name,
        description=f"{task_type.title()} task for {asset.name}",
        status="not_started"
    )
    
    db.add(db_task)
    db.commit()
    db.refresh(db_task)
    
    return TaskStatusInfo(
        task_type=db_task.task_type,
        status=db_task.status,
        task_id=db_task.id,
        assigned_user_id=db_task.assigned_user_id
    )


@router.put("/{asset_id}", response_model=AssetResponse)
async def update_asset(
    asset_id: int,
    asset_update: AssetUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_coordinator_or_admin)
):
    """Update an asset"""
    # Exclude soft deleted assets
    db_asset = db.query(Asset).filter(
        Asset.id == asset_id,
        Asset.deleted_at.is_(None)
    ).first()
    
    if not db_asset:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Asset not found"
        )
    
    # Check project access
    check_project_access(db_asset.project_id, current_user, db)
    
    # Check if new name conflicts with existing assets in the same project
    if asset_update.name and asset_update.name != db_asset.name:
        existing_asset = db.query(Asset).filter(
            Asset.project_id == db_asset.project_id,
            Asset.name == asset_update.name,
            Asset.id != asset_id,
            Asset.deleted_at.is_(None)
        ).first()
        
        if existing_asset:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Asset with this name already exists in the project"
            )
    
    # Update only provided fields
    update_data = asset_update.model_dump(exclude_unset=True)
    for field, value in update_data.items():
        setattr(db_asset, field, value)
    
    db.commit()
    db.refresh(db_asset)
    
    # Add task count (exclude soft deleted tasks)
    task_count = db.query(Task).filter(
        Task.asset_id == db_asset.id,
        Task.deleted_at.is_(None)
    ).count()
    asset_data = AssetResponse.model_validate(db_asset)
    asset_data.task_count = task_count
    
    return asset_data


@router.get("/{asset_id}/deletion-info")
async def get_asset_deletion_info(
    asset_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_coordinator_or_admin)
):
    """Get information about what will be deleted when deleting an asset"""
    # Exclude soft deleted assets
    db_asset = db.query(Asset).filter(
        Asset.id == asset_id,
        Asset.deleted_at.is_(None)
    ).first()
    
    if not db_asset:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Asset not found"
        )
    
    # Check project access
    check_project_access(db_asset.project_id, current_user, db)
    
    # Use the soft deletion service to get comprehensive deletion info
    deletion_service = AssetSoftDeletionService()
    deletion_info = deletion_service.get_deletion_info(asset_id, db)
    
    if not deletion_info:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Asset not found"
        )
    
    return {
        "asset_id": deletion_info.asset_id,
        "asset_name": deletion_info.asset_name,
        "asset_category": deletion_info.asset_category,
        "project_name": deletion_info.project_name,
        "task_count": deletion_info.task_count,
        "submission_count": deletion_info.submission_count,
        "attachment_count": deletion_info.attachment_count,
        "note_count": deletion_info.note_count,
        "review_count": deletion_info.review_count,
        "total_file_size": deletion_info.total_file_size,
        "file_count": deletion_info.file_count,
        "affected_users": deletion_info.affected_users,
        "last_activity_date": deletion_info.last_activity_date,
        "created_at": deletion_info.created_at
    }


@router.delete("/{asset_id}")
async def delete_asset(
    asset_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_coordinator_or_admin)
):
    """Soft delete an asset and all its associated data"""
    # Exclude soft deleted assets
    db_asset = db.query(Asset).filter(
        Asset.id == asset_id,
        Asset.deleted_at.is_(None)
    ).first()
    
    if not db_asset:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Asset not found"
        )
    
    # Check project access
    check_project_access(db_asset.project_id, current_user, db)
    
    # Use the soft deletion service to perform cascading soft deletion
    deletion_service = AssetSoftDeletionService()
    result = deletion_service.soft_delete_asset_cascade(asset_id, db, current_user)
    
    if not result.success:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail={
                "message": "Failed to delete asset",
                "errors": result.errors
            }
        )
    
    # Commit the transaction
    db.commit()
    
    return {
        "message": f"Asset '{result.asset_name}' and all related data have been deleted",
        "asset_id": result.asset_id,
        "asset_name": result.asset_name,
        "deleted_at": result.deleted_at,
        "deleted_by": result.deleted_by,
        "marked_deleted_tasks": result.marked_deleted_tasks,
        "marked_deleted_submissions": result.marked_deleted_submissions,
        "marked_deleted_attachments": result.marked_deleted_attachments,
        "marked_deleted_notes": result.marked_deleted_notes,
        "marked_deleted_reviews": result.marked_deleted_reviews,
        "operation_duration": result.operation_duration
    }