from rest_framework import serializers
from .models import WeekDay, TimeTable
from academics.models import AcademicClass, Subject, SubjectTeacher
from people.models import Teacher
from academics.serializers import AcademicClassSerializer, SubjectSerializer
from people.serializers import SimpleTeacherSerializer
from django.db import IntegrityError


# =====================================================
# WEEK DAY SERIALIZERS
# =====================================================


class WeekDaySerializer(serializers.ModelSerializer):
    """Clean WeekDay Serializer"""

    day_display = serializers.SerializerMethodField()

    class Meta:
        model = WeekDay
        fields = [
            "id",
            "day_code",
            "day_display",
            "day_name",
            "is_open",
            "start_time",
            "end_time",
            "default_period_duration",
            "break_after_periods",
            "break_duration",
            "auto_generate_periods",
            "is_active",
            "created_at",
        ]
        read_only_fields = ["created_at"]

    def get_day_display(self, obj):
        """Get the display value for day_code"""
        return obj.get_day_code_display() if obj.day_code else None

    def validate_day_code(self, value):
        """Validate unique day_code"""
        if value and WeekDay.objects.filter(day_code=value).exists():
            if self.instance and self.instance.day_code == value:
                return value
            raise serializers.ValidationError(
                "A weekday with this code already exists."
            )
        return value


class WeekDayDetailSerializer(WeekDaySerializer):
    """Detailed WeekDay Serializer with timetable entries"""

    timetable_entries = serializers.SerializerMethodField()

    class Meta(WeekDaySerializer.Meta):
        fields = WeekDaySerializer.Meta.fields + ["timetable_entries"]

    def get_timetable_entries(self, obj):
        """Get active timetable entries for this weekday"""
        timetables = obj.timetable_entries.filter(is_active=True).select_related(
            "academic_class", "subject", "subject_teacher", "substitute_teacher"
        )[:20]
        return TimeTableSerializer(timetables, many=True, context=self.context).data


class SimpleWeekDaySerializer(serializers.ModelSerializer):
    """Simple WeekDay Serializer for nested usage"""

    day_display = serializers.SerializerMethodField()

    class Meta:
        model = WeekDay
        fields = [
            "id",
            "day_code",
            "day_display",
            "day_name",
            "is_open",
            "start_time",
            "end_time",
            "is_active",
        ]

    def get_day_display(self, obj):
        """Get the display value for day_code"""
        return obj.get_day_code_display() if obj.day_code else None


# =====================================================
# TIMETABLE SERIALIZERS
# =====================================================


class TimeTableSerializer(serializers.ModelSerializer):
    """Clean TimeTable Serializer"""

    # Read-only fields for related data
    academic_class_name = serializers.SerializerMethodField()
    weekday_name = serializers.CharField(source="weekday.day_name", read_only=True)
    weekday_code = serializers.CharField(source="weekday.day_code", read_only=True)
    subject_name = serializers.CharField(source="subject.name", read_only=True)
    subject_code = serializers.CharField(source="subject.code", read_only=True)
    teacher_name = serializers.SerializerMethodField()
    teacher_id = serializers.SerializerMethodField()
    substitute_teacher_name = serializers.SerializerMethodField()

    # Nested serializers for detailed responses
    academic_class_detail = AcademicClassSerializer(
        source="academic_class", read_only=True
    )
    weekday_detail = SimpleWeekDaySerializer(source="weekday", read_only=True)
    subject_detail = SubjectSerializer(source="subject", read_only=True)
    substitute_teacher_detail = SimpleTeacherSerializer(
        source="substitute_teacher", read_only=True
    )

    # Write-only fields for creating/updating with IDs
    academic_class_id = serializers.PrimaryKeyRelatedField(
        queryset=AcademicClass.objects.all(),
        source="academic_class",
        write_only=True,
        required=False,
        allow_null=True,
    )
    weekday_id = serializers.PrimaryKeyRelatedField(
        queryset=WeekDay.objects.all(),
        source="weekday",
        write_only=True,
        required=False,
        allow_null=True,
    )
    subject_id = serializers.PrimaryKeyRelatedField(
        queryset=Subject.objects.all(),
        source="subject",
        write_only=True,
        required=False,
        allow_null=True,
    )
    subject_teacher_id = serializers.PrimaryKeyRelatedField(
        queryset=SubjectTeacher.objects.all(),
        source="subject_teacher",
        write_only=True,
        required=False,
        allow_null=True,
    )
    substitute_teacher_id = serializers.PrimaryKeyRelatedField(
        queryset=Teacher.objects.all(),
        source="substitute_teacher",
        write_only=True,
        required=False,
        allow_null=True,
    )

    class Meta:
        model = TimeTable
        fields = [
            "id",
            "academic_class",
            "academic_class_id",
            "academic_class_name",
            "academic_class_detail",
            "weekday",
            "weekday_id",
            "weekday_name",
            "weekday_code",
            "weekday_detail",
            "subject",
            "subject_id",
            "subject_name",
            "subject_code",
            "subject_detail",
            "subject_teacher",
            "subject_teacher_id",
            "teacher_name",
            "teacher_id",
            "period_number",
            "start_time",
            "end_time",
            "room_number",
            "is_break",
            "is_substitution",
            "substitute_teacher",
            "substitute_teacher_id",
            "substitute_teacher_name",
            "substitute_teacher_detail",
            "notes",
            "is_active",
            "created_at",
        ]
        read_only_fields = ["created_at"]

    def get_academic_class_name(self, obj):
        """Get formatted academic class name"""
        if obj.academic_class:
            return str(obj.academic_class)
        return None

    def get_teacher_name(self, obj):
        """Get teacher name from subject_teacher"""
        if obj.subject_teacher and obj.subject_teacher.teacher:
            return obj.subject_teacher.teacher.full_name
        return None

    def get_teacher_id(self, obj):
        """Get teacher ID from subject_teacher"""
        if obj.subject_teacher and obj.subject_teacher.teacher:
            return obj.subject_teacher.teacher.id
        return None

    def get_substitute_teacher_name(self, obj):
        """Get substitute teacher name"""
        if obj.substitute_teacher:
            return obj.substitute_teacher.full_name
        return None


class TimeTableCreateUpdateSerializer(serializers.ModelSerializer):
    """Serializer for creating/updating timetable entries"""

    class Meta:
        model = TimeTable
        fields = [
            'id',  # Include id for updates
            'academic_class', 
            'weekday', 
            'subject', 
            'subject_teacher',
            'period_number', 
            'start_time', 
            'end_time', 
            'room_number',
            'is_break', 
            'is_substitution', 
            'substitute_teacher', 
            'notes', 
            'is_active'
        ]

    def validate(self, data):
        is_break = data.get('is_break', False)
        is_substitution = data.get('is_substitution', False)
        subject = data.get('subject')
        subject_teacher = data.get('subject_teacher')
        substitute_teacher = data.get('substitute_teacher')
        academic_class = data.get('academic_class')
        weekday = data.get('weekday')
        period_number = data.get('period_number')

        # Break period validation
        if is_break:
            if subject or subject_teacher:
                raise serializers.ValidationError(
                    "Break periods cannot have subject or teacher assigned."
                )

        # Substitution validation
        if is_substitution and not substitute_teacher:
            raise serializers.ValidationError(
                "Substitution periods must have a substitute teacher assigned."
            )

        # Regular period validation
        if not is_break and not subject:
            raise serializers.ValidationError(
                {"subject": "Subject is required for non-break periods."}
            )

        # Time validation
        start_time = data.get('start_time')
        end_time = data.get('end_time')
        if start_time and end_time and start_time >= end_time:
            raise serializers.ValidationError(
                "End time must be after start time."
            )

        return data

    def create(self, validated_data):
        try:
            return super().create(validated_data)
        except IntegrityError as e:
            if 'unique_together' in str(e) or 'duplicate' in str(e).lower():
                raise serializers.ValidationError(
                    "A timetable entry already exists for this class, day, and period."
                )
            raise e

    def update(self, instance, validated_data):
        try:
            return super().update(instance, validated_data)
        except IntegrityError as e:
            if 'unique_together' in str(e) or 'duplicate' in str(e).lower():
                raise serializers.ValidationError(
                    "A timetable entry already exists for this class, day, and period."
                )
            raise e


class BulkTimeTableSerializer(serializers.Serializer):
    """Serializer for bulk timetable operations"""

    timetables = TimeTableCreateUpdateSerializer(many=True)

    def create(self, validated_data):
        timetables_data = validated_data['timetables']
        created_timetables = []

        for timetable_data in timetables_data:
            serializer = TimeTableCreateUpdateSerializer(data=timetable_data)
            serializer.is_valid(raise_exception=True)
            created_timetables.append(serializer.save())

        return created_timetables

    def to_representation(self, instance):
        if isinstance(instance, list):
            return TimeTableSerializer(instance, many=True).data
        return TimeTableSerializer(instance).data


class SimpleTimeTableSerializer(serializers.ModelSerializer):
    """Simple TimeTable Serializer for nested usage"""

    subject_name = serializers.CharField(source="subject.name", read_only=True)
    teacher_name = serializers.SerializerMethodField()
    weekday_name = serializers.CharField(source="weekday.day_name", read_only=True)

    class Meta:
        model = TimeTable
        fields = [
            "id",
            "period_number",
            "start_time",
            "end_time",
            "subject_name",
            "teacher_name",
            "weekday_name",
            "room_number",
            "is_break",
            "is_substitution",
            "notes",
            "is_active",
        ]

    def get_teacher_name(self, obj):
        """Get teacher name"""
        if obj.is_substitution and obj.substitute_teacher:
            return obj.substitute_teacher.full_name
        elif obj.subject_teacher and obj.subject_teacher.teacher:
            return obj.subject_teacher.teacher.full_name
        return None


# =====================================================
# BULK OPERATION SERIALIZERS
# =====================================================


class BulkTimeTableCreateSerializer(serializers.Serializer):
    """Serializer for bulk creating timetable entries for a class and weekday"""

    academic_class_id = serializers.PrimaryKeyRelatedField(
        queryset=AcademicClass.objects.all()
    )
    weekday_id = serializers.PrimaryKeyRelatedField(queryset=WeekDay.objects.all())
    timetables = TimeTableCreateUpdateSerializer(many=True)

    def validate(self, data):
        """Validate that period numbers are unique within the bulk request"""
        academic_class = data["academic_class_id"]
        weekday = data["weekday_id"]
        timetables = data["timetables"]

        # Check for duplicate period numbers in the request
        period_numbers = [
            t.get("period_number") for t in timetables if t.get("period_number")
        ]
        if len(period_numbers) != len(set(period_numbers)):
            raise serializers.ValidationError(
                "Duplicate period numbers found in the bulk request."
            )

        # Check for conflicts with existing data
        existing_periods = TimeTable.objects.filter(
            academic_class=academic_class,
            weekday=weekday,
            period_number__in=period_numbers,
            is_active=True,
        ).values_list("period_number", flat=True)

        if existing_periods:
            raise serializers.ValidationError(
                f"Periods {list(existing_periods)} already exist for this class and weekday."
            )

        return data

    def create(self, validated_data):
        """Create multiple timetable entries"""
        academic_class = validated_data["academic_class_id"]
        weekday = validated_data["weekday_id"]
        timetables_data = validated_data["timetables"]

        created_timetables = []
        for timetable_data in timetables_data:
            timetable_data["academic_class"] = academic_class
            timetable_data["weekday"] = weekday
            serializer = TimeTableCreateUpdateSerializer(data=timetable_data)
            serializer.is_valid(raise_exception=True)
            created_timetables.append(serializer.save())

        return created_timetables

    def to_representation(self, instance):
        """Custom representation for bulk create response"""
        if isinstance(instance, list):
            return TimeTableSerializer(instance, many=True, context=self.context).data
        return TimeTableSerializer(instance, context=self.context).data


class ClassTimetableSerializer(serializers.Serializer):
    """Serializer for organizing timetable by weekday for a class"""

    academic_class = AcademicClassSerializer(read_only=True)
    weekday = SimpleWeekDaySerializer(read_only=True)
    periods = serializers.SerializerMethodField()

    def get_periods(self, obj):
        """Get periods ordered by period number"""
        periods = obj.get("periods", [])
        return SimpleTimeTableSerializer(periods, many=True).data


class TeacherTimetableSerializer(serializers.Serializer):
    """Serializer for teacher's timetable organized by day"""

    teacher = SimpleTeacherSerializer(read_only=True)
    weekday = SimpleWeekDaySerializer(read_only=True)
    periods = serializers.SerializerMethodField()

    def get_periods(self, obj):
        """Get periods for this teacher on this weekday"""
        periods = obj.get("periods", [])
        return SimpleTimeTableSerializer(periods, many=True).data


# =====================================================
# EXPORT SERIALIZERS
# =====================================================


class TimetableExportSerializer(serializers.ModelSerializer):
    """Serializer for exporting timetable data"""

    academic_class_name = serializers.SerializerMethodField()
    weekday_name = serializers.CharField(source="weekday.day_name", default=None)
    subject_name = serializers.CharField(source="subject.name", default=None)
    teacher_name = serializers.SerializerMethodField()
    substitute_teacher_name = serializers.CharField(
        source="substitute_teacher.full_name", default=None
    )

    class Meta:
        model = TimeTable
        fields = [
            "academic_class_name",
            "weekday_name",
            "period_number",
            "start_time",
            "end_time",
            "subject_name",
            "teacher_name",
            "substitute_teacher_name",
            "room_number",
            "is_break",
            "is_substitution",
            "notes",
        ]

    def get_academic_class_name(self, obj):
        """Get formatted academic class name"""
        return str(obj.academic_class) if obj.academic_class else None

    def get_teacher_name(self, obj):
        """Get teacher name"""
        if obj.is_substitution and obj.substitute_teacher:
            return obj.substitute_teacher.full_name
        elif obj.subject_teacher and obj.subject_teacher.teacher:
            return obj.subject_teacher.teacher.full_name
        return None