from rest_framework import status
from rest_framework.exceptions import ValidationError

from django.db.models import Q, Sum
from django.db.models.functions import Coalesce

from api.models import UserPermission, RolePermission, TtStudent
from api.models.tt_activity import TtActivity
from api.models.tt_staff import TtStaff
from api.views.admin.base import AdminApiBase
from api.validator import BaseValidator
from api.utils import log_critical_error, get_exception_detail, get_setting_academic_term_ids
from api.translation import __


class ActivityList(AdminApiBase):
    def validate_request(self, request):
        rules = {
            "view_self": "nullable",
            "student_set": "nullable",
        }

        attribute = {}

        validator = BaseValidator(request.data, rules, attribute)
        error = validator.validate()
        if error:
            raise ValidationError(error)

    def post(self, request):
        try:
            self.validate_request(request)

            response = {
                "data": self.get_activity_by_date(request.user, request.data)
            }

            return self.api_response(data=response)

        except ValidationError as e:
            first_message = e.detail["error"]
            errors = e.detail["errors"]
            return self.api_response(
                error=first_message,
                errors=errors,
                code=status.HTTP_400_BAD_REQUEST
            )
        except Exception as e:
            e_details = get_exception_detail(e)
            log_critical_error(
                user_id=None,
                descr=e_details["descr"],
                url=e_details["url"],
                trace=e_details["trace"]
            )
            return self.api_response(
                error=__("message.internal_server_error"),
                code=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    def is_only_self_user(self, user):
        return getattr(user, "is_only_self", False)

    def get_self_staff(self, user):
        email = (user.email or "").strip()

        if not email:
            return None

        return (
            TtStaff.objects
            .filter(
                email__iexact=email,
                status=TtStaff.STATUS_TO_CODE["active"],
            )
            .first()
        )

    def get_self_student(self, user):
        email = (user.email or "").strip()

        if not email:
            return None

        return (
            TtStudent.objects
            .filter(
                email__iexact=email,
                status=TtStudent.STATUS_TO_CODE["active"],
            )
            .first()
        )

    def get_activity_by_date(self, user, request_data):
        is_only_self = self.is_only_self_user(user)
        student_set_id = request_data.get("student_set", None)
        
        # if FE got pass in this flag, show self only
        if request_data.get("view_self"):
            is_only_self = True

        self_staff = None
        self_student = None
        if is_only_self:
            self_staff = self.get_self_staff(user)
            if not self_staff:
                self_student = self.get_self_student(user)
                if not self_student:
                    return []
  
        activities = (
            TtActivity.objects
            .filter(
                scheduled=1,
                status=TtActivity.STATUS_TO_CODE["active"],
            )
        )

        setting_academic_term_ids= get_setting_academic_term_ids()
        
        if setting_academic_term_ids:
            activities = activities.filter(
                Q(academic_term_id__in=setting_academic_term_ids) | 
                Q(academic_term_id__isnull=True)
        )

        if is_only_self:
            if self_staff:
                activities = activities.filter(staff__id=self_staff.id).distinct()
            elif self_student:
                activities = activities.filter(student_set__student__id=self_student.id).distinct()
            else:
                return []
        else:
            if student_set_id:
                activities = activities.filter(student_set__id=student_set_id)
            else:
                return []
            
        activities = (
            activities
            .select_related(
                "activity_type",
                "activity_template",
                "department",
                "academic_term",
                "module",
                "week_pattern",
            )
            .prefetch_related(
                "week",
                "week_pattern__week",
                "staff",
                "location",
                "student_set",
            )
            .annotate(
                real_size=Coalesce(Sum("student_set__planned_size"), 0)
            )
            .distinct()
        )

        result = []
        for val in activities:
            data = {
                "id": val.id,
                "code": val.code,
                "name": val.name,
                # "desc": val.desc,
                # "module_desc": val.module.desc if val.module else None,
                # "activity_template": val.activity_template.name if val.activity_template else None,
                # "activity_template_id": val.activity_template_id,
                # "department": val.department.name if val.department else None,
                # "department_id": val.department_id,
                # "academic_term": val.academic_term.name if val.academic_term else None,
                # "academic_term_id": val.academic_term_id,
                "activity_type": val.activity_type.name if val.activity_type else None,
                "activity_type_color": val.activity_type.color if val.activity_type else None,
                # "activity_type_id": val.activity_type_id,
                "module": val.module.name if val.module else None,
                # "module_id": val.module_id,
                "duration": val.duration,
                "slot_required": val.slot_required,
                "planned_size": val.planned_size,
                "real_size": val.real_size,
                "scheduled_start_time": val.scheduled_start_time,
                "scheduled_day": val.scheduled_day,
                "scheduled_start_slot": val.scheduled_start_slot,
                # "scheduled": val.scheduled,
                # "is_jta": val.is_jta,
                # "jta_parent_id": val.jta_parent_id,
                # "is_variant": val.is_variant,
                # "variant_parent_id": val.variant_parent_id,
                "is_booking": val.is_booking,
                # "week_pattern_id": val.week_pattern_id,
                # "week_pattern_name": val.week_pattern.name if val.week_pattern else None,
            }

            if val.week_pattern:
                data["week"] = [
                    {
                        "id": w.id,
                        "week": w.week,
                        "start_date": w.start_date,
                    }
                    for w in val.week_pattern.week.all()
                ] if val.week_pattern else []
            else:
                data["week"] = [
                    {
                        "id": w.id,
                        "week": w.week,
                        "start_date": w.start_date,
                    }
                    for w in val.week.all()
                ]


            data["staff"] = [
                {
                    "id": o.id,
                    "code": o.code,
                    "name": o.name,
                }
                for o in val.staff.all()
            ]

            data["location"] = [
                {
                    "id": o.id,
                    "code": o.code,
                    "name": o.name,
                }
                for o in val.location.all()
            ]

            data["student_set"] = [
                {
                    "id": o.id,
                    "code": o.code,
                    "name": o.name,
                    "planned_size": o.planned_size,
                }
                for o in val.student_set.all()
            ]

            result.append(data)

        return result