from flask import current_app
from src.models import DatabaseContextManager
from src.models.models import (
    CourseLevel, CourseWeek, CourseModule, Course, Tutor, Supervisor, UserType
)
from sqlalchemy import or_, func, case, and_
from src.utils import ApiABC, custom_response
import uuid
from datetime import datetime, timedelta, date

class CourseLevelManager(ApiABC):
    def __init__(self):
        super().__init__(CourseLevel)

    def create(self, payload):
        """Create a new course level"""
        with DatabaseContextManager() as ctx:
            try:
                # Validate required fields
                required_fields = ['course_id', 'level_name', 'requester_id']
                for field in required_fields:
                    if field not in payload or not payload[field]:
                        return custom_response(
                            success=False,
                            data=f"Missing required field: {field}",
                            status_code=400
                        )

                # Verify course exists
                course = ctx.session.query(Course).filter(Course.id == payload['course_id']).first()
                if not course:
                    return custom_response(
                        success=False,
                        data="Course not found",
                        status_code=404
                    )

                # Verify requester has permission
                requester = ctx.session.query(Supervisor).filter(Supervisor.id == payload['requester_id']).first()
                if not requester:
                    return custom_response(
                        success=False,
                        data="Unauthorized - Only supervisors can create course levels",
                        status_code=403
                    )

                # Check if level name already exists for this course
                existing_level = ctx.session.query(CourseLevel).filter(
                    CourseLevel.course_id == payload['course_id'],
                    CourseLevel.level_name == payload['level_name']
                ).first()
                
                if existing_level:
                    return custom_response(
                        success=False,
                        data="A level with this name already exists for this course",
                        status_code=400
                    )

                # Get next level order
                max_order = ctx.session.query(func.max(CourseLevel.level_order)).filter(
                    CourseLevel.course_id == payload['course_id']
                ).scalar() or 0

                # Create new course level
                new_level = CourseLevel(
                    id=str(uuid.uuid4()),
                    course_id=payload['course_id'],
                    level_name=payload['level_name'],
                    level_order=max_order + 1,
                    description=payload.get('description', ''),
                    learning_objectives=payload.get('learning_objectives', ''),
                    estimated_duration_weeks=payload.get('estimated_duration_weeks', 1),
                    is_active=True
                )

                ctx.session.add(new_level)
                ctx.session.commit()

                return custom_response(
                    success=True,
                    data={
                        "message": "Course level created successfully",
                        "level": {
                            "id": new_level.id,
                            "level_name": new_level.level_name,
                            "level_order": new_level.level_order,
                            "description": new_level.description,
                            "learning_objectives": new_level.learning_objectives,
                            "estimated_duration_weeks": new_level.estimated_duration_weeks,
                            "is_active": new_level.is_active,
                            "created_at": new_level.created_at.isoformat()
                        }
                    },
                    status_code=201
                )

            except Exception as e:
                ctx.session.rollback()
                current_app.logger.error(f"Error creating course level: {str(e)}", exc_info=True)
                return custom_response(
                    success=False,
                    data="Failed to create course level due to server error",
                    status_code=500
                )

    def get(self, level_id):
        """Get course level details with weeks and modules"""
        with DatabaseContextManager() as ctx:
            try:
                level = ctx.session.query(CourseLevel).filter(CourseLevel.id == level_id).first()
                if not level:
                    return custom_response(
                        success=False,
                        data="Course level not found",
                        status_code=404
                    )

                # Get weeks with modules
                weeks_data = []
                for week in level.weeks:
                    modules_data = []
                    for module in week.modules:
                        modules_data.append({
                            "id": module.id,
                            "title": module.title,
                            "description": module.description,
                            "module_type": module.module_type,
                            "estimated_duration_minutes": module.estimated_duration_minutes,
                            "learning_objectives": module.learning_objectives,
                            "is_required": module.is_required,
                            "is_published": module.is_published,
                            "is_active": module.is_active,
                            "sequence": module.sequence,
                            "created_at": module.created_at.isoformat(),
                            "updated_at": module.updated_at.isoformat()
                        })

                    weeks_data.append({
                        "id": week.id,
                        "week_number": week.week_number,
                        "week_title": week.week_title,
                        "description": week.description,
                        "learning_outcomes": week.learning_outcomes,
                        "estimated_hours": week.estimated_hours,
                        "start_date": week.start_date.isoformat() if week.start_date else None,
                        "end_date": week.end_date.isoformat() if week.end_date else None,
                        "is_active": week.is_active,
                        "created_at": week.created_at.isoformat(),
                        "updated_at": week.updated_at.isoformat(),
                        "modules": modules_data
                    })

                level_data = {
                    "id": level.id,
                    "course_id": level.course_id,
                    "level_name": level.level_name,
                    "level_order": level.level_order,
                    "description": level.description,
                    "learning_objectives": level.learning_objectives,
                    "estimated_duration_weeks": level.estimated_duration_weeks,
                    "is_active": level.is_active,
                    "created_at": level.created_at.isoformat(),
                    "weeks": weeks_data
                }

                return custom_response(
                    success=True,
                    data=level_data,
                    status_code=200
                )

            except Exception as e:
                current_app.logger.error(f"Error fetching course level: {str(e)}", exc_info=True)
                return custom_response(
                    success=False,
                    data="Failed to fetch course level due to server error",
                    status_code=500
                )

    def update(self, level_id, payload):
        """Update course level details"""
        with DatabaseContextManager() as ctx:
            try:
                level = ctx.session.query(CourseLevel).filter(CourseLevel.id == level_id).first()
                if not level:
                    return custom_response(
                        success=False,
                        data="Course level not found",
                        status_code=404
                    )

                # Update fields
                if 'level_name' in payload:
                    # Check if new name conflicts with existing levels
                    existing_level = ctx.session.query(CourseLevel).filter(
                        CourseLevel.course_id == level.course_id,
                        CourseLevel.level_name == payload['level_name'],
                        CourseLevel.id != level_id
                    ).first()
                    
                    if existing_level:
                        return custom_response(
                            success=False,
                            data="A level with this name already exists for this course",
                            status_code=400
                        )
                    level.level_name = payload['level_name']

                if 'description' in payload:
                    level.description = payload['description']
                if 'learning_objectives' in payload:
                    level.learning_objectives = payload['learning_objectives']
                if 'estimated_duration_weeks' in payload:
                    level.estimated_duration_weeks = payload['estimated_duration_weeks']
                if 'is_active' in payload:
                    level.is_active = payload['is_active']

                ctx.session.commit()

                return custom_response(
                    success=True,
                    data="Course level updated successfully",
                    status_code=200
                )

            except Exception as e:
                ctx.session.rollback()
                current_app.logger.error(f"Error updating course level: {str(e)}", exc_info=True)
                return custom_response(
                    success=False,
                    data="Failed to update course level due to server error",
                    status_code=500
                )

    def delete(self, level_id, requester_id):
        """Delete a course level (soft delete)"""
        with DatabaseContextManager() as ctx:
            try:
                level = ctx.session.query(CourseLevel).filter(CourseLevel.id == level_id).first()
                if not level:
                    return custom_response(
                        success=False,
                        data="Course level not found",
                        status_code=404
                    )

                # Verify requester has permission
                requester = ctx.session.query(Supervisor).filter(Supervisor.id == requester_id).first()
                if not requester:
                    return custom_response(
                        success=False,
                        data="Unauthorized - Only supervisors can delete course levels",
                        status_code=403
                    )

                # Soft delete - deactivate the level
                level.is_active = False
                ctx.session.commit()

                return custom_response(
                    success=True,
                    data="Course level deleted successfully",
                    status_code=200
                )

            except Exception as e:
                ctx.session.rollback()
                current_app.logger.error(f"Error deleting course level: {str(e)}", exc_info=True)
                return custom_response(
                    success=False,
                    data="Failed to delete course level due to server error",
                    status_code=500
                )

    def fetch_all(self, course_id, page=1, per_page=10):
        """Get all course levels for a course with pagination"""
        with DatabaseContextManager() as ctx:
            try:
                # Verify course exists
                course = ctx.session.query(Course).filter(Course.id == course_id).first()
                if not course:
                    return custom_response(
                        success=False,
                        data="Course not found",
                        status_code=404
                    )

                # Get levels with pagination
                query = ctx.session.query(CourseLevel).filter(
                    CourseLevel.course_id == course_id,
                    CourseLevel.is_active == True
                ).order_by(CourseLevel.level_order)

                total = query.count()
                levels = query.offset((page - 1) * per_page).limit(per_page).all()

                levels_data = []
                for level in levels:
                    levels_data.append({
                        "id": level.id,
                        "level_name": level.level_name,
                        "level_order": level.level_order,
                        "description": level.description,
                        "learning_objectives": level.learning_objectives,
                        "estimated_duration_weeks": level.estimated_duration_weeks,
                        "is_active": level.is_active,
                        "weeks_count": len(level.weeks),
                        "created_at": level.created_at.isoformat()
                    })

                return custom_response(
                    success=True,
                    data={
                        "levels": levels_data,
                        "pagination": {
                            "page": page,
                            "per_page": per_page,
                            "total": total,
                            "pages": (total + per_page - 1) // per_page
                        }
                    },
                    status_code=200
                )

            except Exception as e:
                current_app.logger.error(f"Error fetching course levels: {str(e)}", exc_info=True)
                return custom_response(
                    success=False,
                    data="Failed to fetch course levels due to server error",
                    status_code=500
                )

    def get_by_course(self, course_id, user_id, user_type):
        """Get all levels for a course with full structure (weeks and modules)"""
        with DatabaseContextManager() as ctx:
            try:
                # Verify course exists
                course = ctx.session.query(Course).filter(Course.id == course_id).first()
                if not course:
                    return custom_response(
                        success=False,
                        data="Course not found",
                        status_code=404
                    )

                # Verify user has access to this course
                if not self._has_course_access(ctx, course_id, user_id, user_type):
                    return custom_response(
                        success=False,
                        data="Unauthorized - You don't have access to this course",
                        status_code=403
                    )

                # Get levels with weeks and modules
                levels = ctx.session.query(CourseLevel).filter(
                    CourseLevel.course_id == course_id,
                    CourseLevel.is_active == True
                ).order_by(CourseLevel.level_order).all()

                levels_data = []
                for level in levels:
                    weeks_data = []
                    for week in level.weeks:
                        modules_data = []
                        for module in week.modules:
                            modules_data.append({
                                "id": module.id,
                                "title": module.title,
                                "description": module.description,
                                "module_type": module.module_type,
                                "estimated_duration_minutes": module.estimated_duration_minutes,
                                "learning_objectives": module.learning_objectives,
                                "is_required": module.is_required,
                                "is_published": module.is_published,
                                "is_active": module.is_active,
                                "sequence": module.sequence,
                                "created_at": module.created_at.isoformat(),
                                "updated_at": module.updated_at.isoformat()
                            })

                        weeks_data.append({
                            "id": week.id,
                            "week_number": week.week_number,
                            "week_title": week.week_title,
                            "description": week.description,
                            "learning_outcomes": week.learning_outcomes,
                            "estimated_hours": week.estimated_hours,
                            "start_date": week.start_date.isoformat() if week.start_date else None,
                            "end_date": week.end_date.isoformat() if week.end_date else None,
                            "is_active": week.is_active,
                            "created_at": week.created_at.isoformat(),
                            "updated_at": week.updated_at.isoformat(),
                            "modules": modules_data
                        })

                    levels_data.append({
                        "id": level.id,
                        "level_name": level.level_name,
                        "level_order": level.level_order,
                        "description": level.description,
                        "learning_objectives": level.learning_objectives,
                        "estimated_duration_weeks": level.estimated_duration_weeks,
                        "is_active": level.is_active,
                        "created_at": level.created_at.isoformat(),
                        "weeks": weeks_data
                    })

                return custom_response(
                    success=True,
                    data=levels_data,
                    status_code=200
                )

            except Exception as e:
                current_app.logger.error(f"Error fetching course structure: {str(e)}", exc_info=True)
                return custom_response(
                    success=False,
                    data="Failed to fetch course structure due to server error",
                    status_code=500
                )

    def fetchAll(self):
        """Required by ApiABC - not used in this context"""
        return custom_response(
            success=False,
            data="Use get_by_course method instead",
            status_code=400
        )

    def _has_course_access(self, ctx, course_id, user_id, user_type):
        """Check if user has access to the course"""
        try:
            if user_type == UserType.supervisor.value:
                # Supervisors have access to all courses
                return True
            elif user_type == UserType.tutor.value:
                # Check if tutor is assigned to this course
                tutor = ctx.session.query(Tutor).filter(Tutor.id == user_id).first()
                if tutor:
                    return course_id in [course.id for course in tutor.courses]
            elif user_type == UserType.student.value:
                # Check if student is enrolled in this course
                from src.models.models import Enrollment, enrollment_courses
                enrollment = ctx.session.query(Enrollment).join(enrollment_courses).filter(
                    enrollment_courses.c.course_id == course_id,
                    Enrollment.student_id == user_id
                ).first()
                return enrollment is not None
            
            return False
        except Exception as e:
            current_app.logger.error(f"Error checking course access: {str(e)}", exc_info=True)
            return False
