from django.db.models.functions import Concat
from rest_framework import status
from rest_framework.exceptions import ValidationError

from django.db.models import Q, Value, F

from api.models import UserPermission, RolePermission, Setting, TtStudent
from api.models.tt_department import TtDepartment
from api.models.tt_pos import TtPos
from api.models.tt_module import TtModule
from api.models.tt_staff import TtStaff
from api.models.tt_location import TtLocation
from api.models.tt_student_set import TtStudentSet
from api.models.tt_activity import TtActivity
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 ActivityByDateRules(AdminApiBase):
    def validate_request(self, request):
        rules = {
        }

        attribute = {
        }

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

    def get_permission_summary(self, user):
        summary = {
            "department": {
                "show": False,
                "grant_to_all_department": False,
                "department_ids": set(),
            },
            "pos": {
                "show": False,
                "grant_to_all_department": False,
                "department_ids": set(),
            },
            "module": {
                "show": False,
                "grant_to_all_department": False,
                "department_ids": set(),
            },
            "staff": {
                "show": False,
                "grant_to_all_department": False,
                "department_ids": set(),
            },
            "location": {
                "show": False,
                "grant_to_all_department": False,
                "department_ids": set(),
            },
            "student_set": {
                "show": False,
                "grant_to_all_department": False,
                "department_ids": set(),
            },
            "admin_setting": {
                "show": False,
                "grant_to_all_department": False,
                "department_ids": set(),
            },
            "academic_term_setting": {
                "show": False,
                "grant_to_all_department": False,
                "department_ids": set(),
            },
        }

        if user.is_superuser:
            for key in summary:
                summary[key]["show"] = True
                summary[key]["grant_to_all_department"] = True
            return summary

        user_permissions = (
            UserPermission.objects
            .filter(user=user)
            .prefetch_related("userpermissiondepartment_set")
        )
        
        role_ids = list(user.role.values_list("id", flat=True))
        role_permissions = (
            RolePermission.objects
            .filter(role_id__in=role_ids)
            .prefetch_related("rolepermissiondepartment_set")
        )

        for row in user_permissions:
            if row.permission not in summary:
                continue

            item = summary[row.permission]
            item["show"] = True

            if row.grant_to_all_department:
                item["grant_to_all_department"] = True
            else:
                item["department_ids"].update(
                    row.userpermissiondepartment_set.values_list("department_id", flat=True)
                )

        for row in role_permissions:
            if row.permission not in summary:
                continue

            item = summary[row.permission]
            item["show"] = True

            if row.grant_to_all_department:
                item["grant_to_all_department"] = True
            else:
                item["department_ids"].update(
                    row.rolepermissiondepartment_set.values_list("department_id", flat=True)
                )

        return summary

    def get_shared_resource_q(self, permission_item):
        if not permission_item["show"]:
            return Q(pk__in=[])

        if permission_item["grant_to_all_department"]:
            return Q()

        department_ids = permission_item["department_ids"]

        return (
                Q(department_id__in=department_ids) |
                Q(shared_with_department__id__in=department_ids) |
                Q(shared_with_all_department=True)
        )

    def post(self, request):
        try:
            self.validate_request(request)
            user = request.user
            data = self.get_rules(user)
            data["is_only_self"] = user.is_only_self
            data['view_self_flag'] = False
            # even is_only_self is False, if can link the user with staff table, also can show self timetabler
            if not data["is_only_self"]:
                self_staff = self.get_self_staff(user)

                if not self_staff:
                    self_student = self.get_self_student(user)
                    if self_student:
                        data['view_self_flag'] = True
                else:
                    data['view_self_flag'] = True
            response = {
                "rules": 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_rules(self, user):
        data = {}
        permission_summary = self.get_permission_summary(user)
        setting_academic_term_ids = get_setting_academic_term_ids()
        permission = ["admin_setting", "academic_term_setting"] 
        for key in permission:
            data[key] = False
            if permission_summary[key]["show"]:
                data[key] = True

        setting_params = {
            "minute_per_slot",
            "slot_per_day",
            "slot_per_week",
            # "academic_term",
        }

        if self.is_only_self_user(user):
            data["based_setting"] = Setting.get_multiple_setting(setting_params)
            return data

        if permission_summary["department"]["show"]:
            qs = TtDepartment.objects.filter(status=TtDepartment.STATUS_TO_CODE["active"])

            if not permission_summary["department"]["grant_to_all_department"]:
                qs = qs.filter(id__in=permission_summary["department"]["department_ids"])

            data["department"] = list(
                qs.order_by("name").values("id", "code", "name")
            )

        if permission_summary["pos"]["show"]:
            qs = TtPos.objects.filter(status=TtPos.STATUS_TO_CODE["active"])

            if setting_academic_term_ids:
                qs = qs.filter(academic_term_id__in=setting_academic_term_ids)

            if not permission_summary["pos"]["grant_to_all_department"]:
                qs = qs.filter(department_id__in=permission_summary["pos"]["department_ids"])

            data["programme_of_study"] = list(
                qs.order_by("name").values("id", "code", "name")
            )

        if permission_summary["module"]["show"]:
            qs = TtModule.objects.filter(status=TtModule.STATUS_TO_CODE["active"])

            if setting_academic_term_ids:
                qs = qs.filter(academic_term_id__in=setting_academic_term_ids)

            if not permission_summary["module"]["grant_to_all_department"]:
                qs = qs.filter(department_id__in=permission_summary["module"]["department_ids"])

            module_rows = (
                qs.order_by("name")
                .annotate(
                    display_name=Concat("name", Value(" ("), "code", Value(")"))
                )
                .values("id", "code", "display_name")
            )

            data["module"] = [
                {"id": row["id"], "code": row["code"], "name": row["display_name"]}
                for row in module_rows
            ]

        if permission_summary["staff"]["show"]:
            activity_qs = TtActivity.objects.filter(
                status=TtActivity.STATUS_TO_CODE["active"],
                scheduled=1,
                staff__isnull=False,
            )

            if setting_academic_term_ids:
                # booking normally no academic_term, so booking is always need to get
                activity_qs = activity_qs.filter(
                    Q(academic_term_id__in=setting_academic_term_ids) | Q(academic_term__id__isnull=True)
                )

            staff_ids = activity_qs.values_list("staff__id", flat=True).distinct()

            qs = TtStaff.objects.filter(
                id__in=staff_ids,
                status=TtStaff.STATUS_TO_CODE["active"],
            )

            qs = qs.filter(
                self.get_shared_resource_q(permission_summary["staff"])
            ).distinct()

            data["staff"] = list(
                qs.order_by("name").values("id", "code", "name")
            )

        if permission_summary["location"]["show"]:
            activity_qs = TtActivity.objects.filter(
                status=TtActivity.STATUS_TO_CODE["active"],
                scheduled=1,
                location__isnull=False,
            )

            if setting_academic_term_ids:
                # booking normally no academic_term, so booking is always need to get
                activity_qs = activity_qs.filter(
                    Q(academic_term_id__in=setting_academic_term_ids) | Q(academic_term__id__isnull=True)
                )

            location_ids = activity_qs.values_list("location__id", flat=True).distinct()

            qs = TtLocation.objects.filter(
                id__in=location_ids,
                status=TtLocation.STATUS_TO_CODE["active"],
            )

            qs = qs.filter(
                self.get_shared_resource_q(permission_summary["location"])
            ).distinct()

            data["location"] = list(
                qs.order_by("name").values("id", "code", "name")
            )

        if permission_summary["student_set"]["show"]:
            activity_qs = TtActivity.objects.filter(
                status=TtActivity.STATUS_TO_CODE["active"],
                scheduled=1,
                student_set__isnull=False,
            )

            if setting_academic_term_ids:
                activity_qs = activity_qs.filter(academic_term_id__in=setting_academic_term_ids)

            student_set_ids = activity_qs.values_list("student_set__id", flat=True).distinct()

            qs = TtStudentSet.objects.filter(
                id__in=student_set_ids,
                status=TtStudentSet.STATUS_TO_CODE["active"],
            )
            # department filter need check here, cause possible student set have different department with the activity, so cant check from activity
            if not permission_summary["student_set"]["grant_to_all_department"]:
                qs = qs.filter(
                    department_id__in=permission_summary["student_set"]["department_ids"]
                )

            data["student_set"] = list(
                qs.order_by("name").values("id", "code", "name")
            )

        data['based_setting'] = Setting.get_multiple_setting(setting_params)
        data['based_setting']['academic_term'] = setting_academic_term_ids
        return data