from flask import request, jsonify, current_app
from functools import wraps
from datetime import datetime
from flask_smorest import Blueprint
from src.views.speciality.utils import speciality_manager
from src.utils import custom_response

# Create Blueprint
speciality_bp = Blueprint('speciality', __name__, url_prefix='/speciality')

# Enhanced error handler decorator
def handle_speciality_errors(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except Exception as e:
            current_app.logger.error(f"Speciality endpoint error: {str(e)}")
            return custom_response(
                success=False,
                data="An unexpected error occurred while processing your request", 
                status_code=500
            )
    return decorated_function

@speciality_bp.route('/manage', methods=['GET'])
@handle_speciality_errors
def get_specialities():
    """
    Get all specialities with enhanced filtering and styling
    Filtered by supervisor's department if supervisor_id is provided
    Supports pagination for large datasets
    """
    try:
        # Extract query parameters for filtering
        filters = {}
        
        # Supervisor ID filter - this will filter by supervisor's department
        supervisor_id = request.args.get('supervisor_id')
        if supervisor_id:
            filters['supervisor_id'] = supervisor_id
        
        # Search filter
        if request.args.get('search'):
            filters['search'] = request.args.get('search')
        
        # Department filter - support multiple departments
        departments = request.args.getlist('departments')  # Get multiple department values
        if departments:
            filters['departments'] = departments
        elif request.args.get('department'):  # Single department for backward compatibility
            filters['department'] = request.args.get('department')
        
        # Status filter
        if request.args.get('status') in ['active', 'inactive']:
            filters['is_active'] = request.args.get('status') == 'active'
        
        # Sharing capable filter
        if request.args.get('sharing_capable') == 'true':
            filters['sharing_capable'] = True
        
        # Pagination parameters - use higher default limit for frontend components
        page = request.args.get('page', 1, type=int)
        per_page = request.args.get('per_page', 1000, type=int)  # Increased default from 10 to 1000
        
        # Validate pagination parameters
        if page < 1:
            page = 1
        if per_page < 1 or per_page > 1000:  # Increased max limit
            per_page = 1000
        
        filters['page'] = page
        filters['per_page'] = per_page
        
        # Get specialities with filters and pagination
        result = speciality_manager.get_all(filters)
        
        return result
        
    except Exception as e:
        current_app.logger.error(f"Error in get_specialities: {str(e)}")
        return custom_response(
            success=False,
            data="Failed to retrieve specialities",
            status_code=500
        )

@speciality_bp.route('/manage', methods=['POST'])
@handle_speciality_errors
def create_speciality():
    """
    Create a new speciality with enhanced validation and styling
    """
    try:
        # Get request data
        payload = request.get_json()
        
        if not payload:
            return custom_response(
                success=False,
                data="Request body is required",
                status_code=400
            )
        
        # Enhanced validation
        required_fields = ['name', 'department']
        missing_fields = [field for field in required_fields if not payload.get(field)]
        
        if missing_fields:
            return custom_response(
                success=False,
                data=f"Missing required fields: {', '.join(missing_fields)}", 
                status_code=400
            )
        
        # Validate name length
        if len(payload['name'].strip()) < 2:
            return custom_response(
                success=False,
                data="Speciality name must be at least 2 characters long",
                status_code=400
            )
        
        if len(payload['name'].strip()) > 200:
            return custom_response(
                success=False,
                data="Speciality name cannot exceed 200 characters",
                status_code=400
            )
        
        # Validate department
        valid_departments = [
            'Agriculture', 'Electrical', 'Computing & Informatics', 'Health Sciences', 'Building Technology',
            'Liberal Studies', 'Business', 'Applied Science', 'Fashion & Design', 'Hospitality', 'Cosmetology', 'Mechanical'
        ]
        
        if payload['department'] not in valid_departments:
            return custom_response(
                success=False,
                data=f"Invalid department. Must be one of: {', '.join(valid_departments)}", 
                status_code=400
            )
        
        # Create speciality
        result = speciality_manager.create(payload)
        
        return result
        
    except Exception as e:
        current_app.logger.error(f"Error in create_speciality: {str(e)}")
        return custom_response(
            success=False,
            data="Failed to create speciality",
            status_code=500
        )

@speciality_bp.route('/manage/<speciality_id>', methods=['GET'])
@handle_speciality_errors
def get_speciality(speciality_id):
    """
    Get a specific speciality by ID with enhanced details and styling
    """
    try:
        # Validate speciality ID format
        if not speciality_id or len(speciality_id) < 10:
            return custom_response(
                success=False,
                data="Invalid speciality ID format",
                status_code=400
            )
        
        # Get speciality
        result = speciality_manager.get_by_id(speciality_id)
        
        return result
        
    except Exception as e:
        current_app.logger.error(f"Error in get_speciality: {str(e)}")
        return custom_response(
            success=False,
            data="Failed to retrieve speciality",
            status_code=500
        )

@speciality_bp.route('/manage/<speciality_id>', methods=['PUT'])
@handle_speciality_errors
def update_speciality(speciality_id):
    """
    Update a speciality with enhanced validation and styling
    """
    try:
        # Validate speciality ID format
        if not speciality_id or len(speciality_id) < 10:
            return custom_response(
                success=False,
                data="Invalid speciality ID format",
                status_code=400
            )
        
        # Get request data
        payload = request.get_json()
        
        if not payload:
            return custom_response(
                success=False,
                data="Request body is required",
                status_code=400
            )
        
        # Enhanced validation for updates
        if 'name' in payload and payload['name']:
            if len(payload['name'].strip()) < 2:
                return custom_response(
                    success=False,
                    data="Speciality name must be at least 2 characters long",
                    status_code=400
                )
            
            if len(payload['name'].strip()) > 200:
                return custom_response(
                    success=False,
                    data="Speciality name must not exceed 200 characters",
                    status_code=400
                )
        
        if 'department' in payload and payload['department']:
            valid_departments = [
                'Agriculture', 'Electrical', 'Computing & Informatics', 'Health Sciences', 'Building Technology',
                'Liberal Studies', 'Business', 'Applied Science', 'Fashion & Design', 'Hospitality', 'Cosmetology', 'Mechanical'
            ]
            
            if payload['department'] not in valid_departments:
                return custom_response(
                    success=False,
                    data=f"Invalid department. Must be one of: {', '.join(valid_departments)}", 
                    status_code=400
                )
        
        # Update speciality
        result = speciality_manager.update(speciality_id, payload)
        
        return result
        
    except Exception as e:
        current_app.logger.error(f"Error in update_speciality: {str(e)}")
        return custom_response(
            success=False,
            data="Failed to update speciality",
            status_code=500
        )

@speciality_bp.route('/manage/<speciality_id>', methods=['DELETE'])
@handle_speciality_errors
def delete_speciality(speciality_id):
    """
    Delete a speciality and all associated course data with enhanced validation and safety checks.
    This performs a complete cascade deletion of all related data including courses, modules, 
    assignments, timetable blocks, teaching sessions, enrollments, and associations.
    """
    try:
        # Validate speciality ID format
        if not speciality_id or len(speciality_id) < 10:
            return custom_response(
                success=False,
                data="Invalid speciality ID format",
                status_code=400
            )
        
        # Delete speciality
        result = speciality_manager.delete(speciality_id)
        
        return result
        
    except Exception as e:
        current_app.logger.error(f"Error in delete_speciality: {str(e)}")
        return custom_response(
            success=False,
            data="Failed to delete speciality",
            status_code=500
        )

@speciality_bp.route('/manage/bulk', methods=['POST'])
@handle_speciality_errors
def bulk_operations():
    """
    Perform bulk operations on specialities with enhanced styling
    """
    try:
        # Get request data
        payload = request.get_json()
        
        if not payload or 'operations' not in payload:
            return custom_response(
                success=False,
                data="Operations array is required",
                status_code=400
            )
        
        operations = payload['operations']
        
        if not isinstance(operations, list) or len(operations) == 0:
            return custom_response(
                success=False,
                data="Operations must be a non-empty array",
                status_code=400
            )
        
        if len(operations) > 50:
            return custom_response(
                success=False,
                data="Maximum 50 operations allowed per request",
                status_code=400
            )
        
        # Validate each operation
        valid_operations = ['activate', 'deactivate']
        for operation in operations:
            if not isinstance(operation, dict):
                return custom_response(
                    success=False,
                    data="Each operation must be an object",
                    status_code=400
                )
            
            if 'type' not in operation or 'id' not in operation:
                return custom_response(
                    success=False,
                    data="Each operation must have 'type' and 'id' fields",
                    status_code=400
                )
            
            if operation['type'] not in valid_operations:
                return custom_response(
                    success=False,
                    data=f"Invalid operation type. Must be one of: {', '.join(valid_operations)}", 
                    status_code=400
                )
            
            if not operation['id'] or len(operation['id']) < 10:
                return custom_response(
                    success=False,
                    data="Invalid speciality ID in operation",
                    status_code=400
                )
        
        # Perform bulk operations
        result = speciality_manager.bulk_operations(operations)
        
        return result
        
    except Exception as e:
        current_app.logger.error(f"Error in bulk_operations: {str(e)}")
        return custom_response(
            success=False,
            data="Failed to perform bulk operations",
            status_code=500
        )

@speciality_bp.route('/manage/statistics', methods=['GET'])
@handle_speciality_errors
def get_statistics():
    """
    Get comprehensive statistics for specialities with enhanced styling
    """
    try:
        # Get statistics
        result = speciality_manager.get_statistics()
        
        return result
        
    except Exception as e:
        current_app.logger.error(f"Error in get_statistics: {str(e)}")
        return custom_response(
            success=False,
            data="Failed to retrieve statistics",
            status_code=500
        )

@speciality_bp.route('/manage/departments', methods=['GET'])
@handle_speciality_errors
def get_departments():
    """
    Get all available departments with speciality counts
    """
    try:
        # Get specialities to extract departments
        result = speciality_manager.get_all()
        
        # Since result is a Response object, we need to get its JSON data
        result_data = result.json
        
        if not result_data.get('success'):
            return result
        
        specialities = result_data.get('data', {}).get('specialities', [])
        
        # Group by department
        department_stats = {}
        for speciality in specialities:
            dept = speciality['department']
            if dept not in department_stats:
                department_stats[dept] = {
                    'name': dept,
                    'specialities_count': 0,
                    'courses_count': 0,
                    'active_specialities': 0
                }
            
            department_stats[dept]['specialities_count'] += 1
            department_stats[dept]['courses_count'] += speciality.get('courses_count', 0)
            
            if speciality['is_active']:
                department_stats[dept]['active_specialities'] += 1
        
        # Convert to list and sort
        departments_list = list(department_stats.values())
        departments_list.sort(key=lambda x: x['name'])
        
        return custom_response(
            success=True,
            data={
                'departments': departments_list,
                'total_departments': len(departments_list)
            },
            status_code=200
        )
        
    except Exception as e:
        current_app.logger.error(f"Error in get_departments: {str(e)}")
        return custom_response(
            success=False,
            data="Failed to retrieve departments",
            status_code=500
        )

@speciality_bp.route('/manage/search', methods=['GET'])
@handle_speciality_errors
def search_specialities():
    """
    Advanced search for specialities with enhanced filtering and styling
    """
    try:
        # Extract search parameters
        query = request.args.get('q', '').strip()
        department = request.args.get('department', '').strip()
        status = request.args.get('status', '').strip()
        limit = request.args.get('limit', '20')
        
        # Validate limit
        try:
            limit = int(limit)
            if limit < 1 or limit > 100:
                limit = 20
        except ValueError:
            limit = 20
        
        # Build filters
        filters = {}
        
        if query:
            filters['search'] = query
        
        if department:
            filters['department'] = department
        
        if status in ['active', 'inactive']:
            filters['is_active'] = status == 'active'
        
        # Get specialities with filters
        result = speciality_manager.get_all(filters)
        
        if not result.get('success'):
            return result
        
        specialities = result.get('data', {}).get('specialities', [])
        
        # Apply limit
        if len(specialities) > limit:
            specialities = specialities[:limit]
        
        # Enhanced search response
        search_response = {
            'query': query,
            'filters_applied': filters,
            'results_count': len(specialities),
            'total_available': result.get('data', {}).get('statistics', {}).get('total_specialities', 0),
            'specialities': specialities,
            'search_metadata': {
                'search_timestamp': datetime.utcnow().isoformat(),
                'limit_applied': limit,
                'has_more_results': len(specialities) == limit
            }
        }
        
        return custom_response(
            success=True,
            data=search_response,
            status_code=200
        )
        
    except Exception as e:
        current_app.logger.error(f"Error in search_specialities: {str(e)}")
        return custom_response(
            success=False,
            data="Failed to search specialities",
            status_code=500
        )

@speciality_bp.route('/manage/<speciality_id>/analytics', methods=['GET'])
@handle_speciality_errors
def get_speciality_analytics(speciality_id):
    if not speciality_id or len(speciality_id) < 10:
        return custom_response(success=False, data="Invalid speciality ID format", status_code=400)
    return speciality_manager.get_analytics(speciality_id)

@speciality_bp.route('/manage/<speciality_id>/toggle-status', methods=['PATCH'])
@handle_speciality_errors
def toggle_speciality_status(speciality_id):
    """
    Toggle the active status of a speciality
    """
    try:
        # Validate speciality ID format
        if not speciality_id or len(speciality_id) < 10:
            return custom_response(
                success=False,
                data="Invalid speciality ID format",
                status_code=400
            )
        
        # Get request data
        payload = request.get_json()
        
        if not payload or 'is_active' not in payload:
            return custom_response(
                success=False,
                data="Request body must contain 'is_active' field",
                status_code=400
            )
        
        # Validate is_active field
        if not isinstance(payload['is_active'], bool):
            return custom_response(
                success=False,
                data="'is_active' field must be a boolean value",
                status_code=400
            )
        
        # Toggle speciality status
        result = speciality_manager.toggle_status(speciality_id, payload['is_active'])
        
        return result
        
    except Exception as e:
        current_app.logger.error(f"Error in toggle_speciality_status: {str(e)}")
        return custom_response(
            success=False,
            data="Failed to toggle speciality status",
            status_code=500
        )

@speciality_bp.route('/manage/delete-by-department', methods=['DELETE'])
@handle_speciality_errors
def delete_specialities_by_department():
    """
    Delete all specialities under a given department.
    Body JSON: { "department": "Hospitality" }
    """
    try:
        payload = request.get_json() or {}
        department = (payload.get('department') or '').strip()
        if not department:
            return custom_response(
                success=False,
                data="'department' is required",
                status_code=400
            )

        result = speciality_manager.delete_by_department(department)
        return result
    except Exception as e:
        current_app.logger.error(f"Error in delete_specialities_by_department: {str(e)}")
        return custom_response(
            success=False,
            data="Failed to delete specialities by department",
            status_code=500
        )

@speciality_bp.route('/manage/<speciality_id>/students', methods=['GET'])
@handle_speciality_errors
def get_speciality_students(speciality_id):
    """
    Get all students in a speciality with optional filtering and pagination
    """
    try:
        # Validate speciality ID format
        if not speciality_id or len(speciality_id) < 10:
            return custom_response(
                success=False,
                data="Invalid speciality ID format",
                status_code=400
            )
        
        # Extract filters from query parameters
        filters = {}
        
        # Search filter
        if request.args.get('search'):
            filters['search'] = request.args.get('search')
        
        # Year of study filter
        if request.args.get('year_of_study'):
            try:
                filters['year_of_study'] = int(request.args.get('year_of_study'))
            except ValueError:
                return custom_response(
                    success=False,
                    data="year_of_study must be a valid integer",
                    status_code=400
                )
        
        # Program filter
        if request.args.get('program'):
            filters['program'] = request.args.get('program')
        
        # Pagination
        if request.args.get('page'):
            try:
                filters['page'] = int(request.args.get('page'))
            except ValueError:
                return custom_response(
                    success=False,
                    data="page must be a valid integer",
                    status_code=400
                )
        
        if request.args.get('per_page'):
            try:
                filters['per_page'] = int(request.args.get('per_page'))
            except ValueError:
                return custom_response(
                    success=False,
                    data="per_page must be a valid integer",
                    status_code=400
                )
        
        return speciality_manager.get_speciality_students(speciality_id, filters)
        
    except Exception as e:
        current_app.logger.error(f"Error in get_speciality_students endpoint: {str(e)}")
        return custom_response(
            success=False,
            data="An unexpected error occurred while fetching students",
            status_code=500
        )

@speciality_bp.route('/manage/<speciality_id>/students', methods=['POST'])
@handle_speciality_errors
def create_speciality_student(speciality_id):
    """
    Create a new student and assign them to the specified speciality
    """
    try:
        # Validate speciality ID format
        if not speciality_id or len(speciality_id) < 10:
            return custom_response(
                success=False,
                data="Invalid speciality ID format",
                status_code=400
            )
        
        # Get request data
        data = request.get_json()
        if not data:
            return custom_response(
                success=False,
                data="Request body is required",
                status_code=400
            )
        
        # Validate required fields
        required_fields = ['student_id', 'email', 'password_hash', 'first_name', 'last_name', 'program']
        for field in required_fields:
            if not data.get(field):
                return custom_response(
                    success=False,
                    data=f"{field} is required",
                    status_code=400
                )
        
        # Validate email format
        import re
        email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        if not re.match(email_pattern, data['email']):
            return custom_response(
                success=False,
                data="Invalid email format",
                status_code=400
            )
        
        # Validate year of study if provided
        if data.get('year_of_study') and not isinstance(data.get('year_of_study'), int):
            return custom_response(
                success=False,
                data="year_of_study must be an integer",
                status_code=400
            )
        
        return speciality_manager.create_student(speciality_id, data)
        
    except Exception as e:
        current_app.logger.error(f"Error in create_speciality_student endpoint: {str(e)}")
        return custom_response(
            success=False,
            data="An unexpected error occurred while creating student",
            status_code=500
        )

# Health check endpoint
@speciality_bp.route('/health', methods=['GET'])
def health_check():
    """
    Health check endpoint for speciality management service
    """
    try:
        return custom_response(
            success=True,
            data={
                'service': 'Speciality Management',
                'status': 'healthy',
                'timestamp': datetime.utcnow().isoformat(),
                'version': '1.0.0',
                'endpoints': [
                    'GET /speciality/manage',
                    'POST /speciality/manage',
                    'GET /speciality/manage/<id>',
                    'PUT /speciality/manage/<id>',
                    'DELETE /speciality/manage/<id>',
                    'PATCH /speciality/manage/<id>/toggle-status',
                    'POST /speciality/manage/bulk',
                    'GET /speciality/manage/statistics',
                    'GET /speciality/manage/departments',
                    'GET /speciality/manage/search',
                    'GET /speciality/manage/<id>/analytics',
                    'GET /speciality/manage/<id>/students',
                    'POST /speciality/manage/<id>/students'
                ]
            },
            status_code=200
        )
    except Exception as e:
        current_app.logger.error(f"Health check error: {str(e)}")
        return custom_response(
            success=False,
            data="Service health check failed",
            status_code=500
        )