from flask import current_app
from src.models import DatabaseContextManager
from src.models.models import (
    CourseWeek, CourseLevel, 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 CourseWeekManager(ApiABC):
    def __init__(self):
        super().__init__(CourseWeek)

    def create(self, payload):
        """Create a new course week"""
        with DatabaseContextManager() as ctx:
            try:
                # Validate required fields
                required_fields = ['course_level_id', 'week_number', 'week_title', '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 level exists
                level = ctx.session.query(CourseLevel).filter(CourseLevel.id == payload['course_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 == payload['requester_id']).first()
                if not requester:
                    return custom_response(
                        success=False,
                        data="Unauthorized - Only supervisors can create course weeks",
                        status_code=403
                    )

                # Check if week number already exists for this level
                existing_week = ctx.session.query(CourseWeek).filter(
                    CourseWeek.course_level_id == payload['course_level_id'],
                    CourseWeek.week_number == payload['week_number']
                ).first()
                
                if existing_week:
                    return custom_response(
                        success=False,
                        data="A week with this number already exists for this level",
                        status_code=400
                    )

                # Create new course week
                new_week = CourseWeek(
                    id=str(uuid.uuid4()),
                    course_level_id=payload['course_level_id'],
                    week_number=payload['week_number'],
                    week_title=payload['week_title'],
                    description=payload.get('description', ''),
                    learning_outcomes=payload.get('learning_outcomes', ''),
                    estimated_hours=payload.get('estimated_hours', 0.0),
                    start_date=datetime.strptime(payload['start_date'], '%Y-%m-%d').date() if payload.get('start_date') else None,
                    end_date=datetime.strptime(payload['end_date'], '%Y-%m-%d').date() if payload.get('end_date') else None,
                    is_active=True
                )

                ctx.session.add(new_week)
                ctx.session.commit()

                return custom_response(
                    success=True,
                    data={
                        "message": "Course week created successfully",
                        "week": {
                            "id": new_week.id,
                            "week_number": new_week.week_number,
                            "week_title": new_week.week_title,
                            "description": new_week.description,
                            "learning_outcomes": new_week.learning_outcomes,
                            "estimated_hours": new_week.estimated_hours,
                            "start_date": new_week.start_date.isoformat() if new_week.start_date else None,
                            "end_date": new_week.end_date.isoformat() if new_week.end_date else None,
                            "is_active": new_week.is_active,
                            "created_at": new_week.created_at.isoformat()
                        }
                    },
                    status_code=201
                )

            except ValueError as e:
                return custom_response(
                    success=False,
                    data="Invalid date format. Use YYYY-MM-DD",
                    status_code=400
                )
            except Exception as e:
                ctx.session.rollback()
                current_app.logger.error(f"Error creating course week: {str(e)}", exc_info=True)
                return custom_response(
                    success=False,
                    data="Failed to create course week due to server error",
                    status_code=500
                )

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

                # Get modules
                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()
                    })

                week_data = {
                    "id": week.id,
                    "course_level_id": week.course_level_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
                }

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

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

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

                # Update fields
                if 'week_number' in payload:
                    # Check if new week number conflicts with existing weeks
                    existing_week = ctx.session.query(CourseWeek).filter(
                        CourseWeek.course_level_id == week.course_level_id,
                        CourseWeek.week_number == payload['week_number'],
                        CourseWeek.id != week_id
                    ).first()
                    
                    if existing_week:
                        return custom_response(
                            success=False,
                            data="A week with this number already exists for this level",
                            status_code=400
                        )
                    week.week_number = payload['week_number']

                if 'week_title' in payload:
                    week.week_title = payload['week_title']
                if 'description' in payload:
                    week.description = payload['description']
                if 'learning_outcomes' in payload:
                    week.learning_outcomes = payload['learning_outcomes']
                if 'estimated_hours' in payload:
                    week.estimated_hours = payload['estimated_hours']
                if 'start_date' in payload:
                    week.start_date = datetime.strptime(payload['start_date'], '%Y-%m-%d').date() if payload['start_date'] else None
                if 'end_date' in payload:
                    week.end_date = datetime.strptime(payload['end_date'], '%Y-%m-%d').date() if payload['end_date'] else None
                if 'is_active' in payload:
                    week.is_active = payload['is_active']

                ctx.session.commit()

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

            except ValueError as e:
                return custom_response(
                    success=False,
                    data="Invalid date format. Use YYYY-MM-DD",
                    status_code=400
                )
            except Exception as e:
                ctx.session.rollback()
                current_app.logger.error(f"Error updating course week: {str(e)}", exc_info=True)
                return custom_response(
                    success=False,
                    data="Failed to update course week due to server error",
                    status_code=500
                )

    def delete(self, week_id, requester_id):
        """Delete a course week (soft delete)"""
        with DatabaseContextManager() as ctx:
            try:
                week = ctx.session.query(CourseWeek).filter(CourseWeek.id == week_id).first()
                if not week:
                    return custom_response(
                        success=False,
                        data="Course week 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 weeks",
                        status_code=403
                    )

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

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

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

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

                # Get weeks with pagination
                query = ctx.session.query(CourseWeek).filter(
                    CourseWeek.course_level_id == course_level_id,
                    CourseWeek.is_active == True
                ).order_by(CourseWeek.week_number)

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

                weeks_data = []
                for week in weeks:
                    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,
                        "modules_count": len(week.modules),
                        "created_at": week.created_at.isoformat()
                    })

                return custom_response(
                    success=True,
                    data={
                        "weeks": weeks_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 weeks: {str(e)}", exc_info=True)
                return custom_response(
                    success=False,
                    data="Failed to fetch course weeks due to server error",
                    status_code=500
                )

    def get_by_level(self, level_id):
        """Get all weeks for a course level with modules"""
        with DatabaseContextManager() as ctx:
            try:
                # Verify course level exists
                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 = ctx.session.query(CourseWeek).filter(
                    CourseWeek.course_level_id == level_id,
                    CourseWeek.is_active == True
                ).order_by(CourseWeek.week_number).all()

                weeks_data = []
                for week in 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
                    })

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

            except Exception as e:
                current_app.logger.error(f"Error fetching course weeks: {str(e)}", exc_info=True)
                return custom_response(
                    success=False,
                    data="Failed to fetch course weeks 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_level method instead",
            status_code=400
        )
