import re

from django.db.models import Count
from rest_framework import status

from api.helper.resource_map_helper import helper_recalculate_and_get_redis_resource_map
from api.models import TtSetting, AuditTrail, AuditTrailDetails, TtActivity, TtActivityWeek
from api.views.admin.base import AdminApiBase
from api.validator import BaseValidator
from api.utils import log_critical_error, get_exception_detail, \
    bulk_sync_to_redis, get_ip, recalculate_resource_map, get_resource_map_redis_data, \
    remove_variant_activity_name_range, format_variant_activity_name_week_pattern_to_ranges
from rest_framework.exceptions import ValidationError
from api.translation import __
from backend.kafka import send_request
from backend.redis_client import redis_client


class BookingDelete(AdminApiBase):
    def validate_request(self,request):
        rules = {
            "id": "required|array|exists:api.TtActivity,id",
        }
        # if need rename field, can put here
        attribute = {
            # "email": __("attr.email"),
        }
        # validate id first, cause need get activity by id, if wrong id direct return error
        validator = BaseValidator(request.data,rules,attribute)
        error = validator.validate()
        if error:
            raise ValidationError(error)

        activities = TtActivity.objects.filter(id__in=request.data.get("id"))
        for activity in activities:
            if not activity.is_booking:
                raise ValidationError({
                    "error": __("validation.invalid",field=__("attr.activity")),
                    "errors": {"id": __("validation.invalid",field=__("attr.activity"))}
                })

    def post(self, request):
        # Validate input
        try:
            setting_params = {
                "slot_per_week",
                "minute_per_slot",
            }
            settings = TtSetting.get_multiple_setting(setting_params)
            self.validate_request(request)

            action = "booking_delete"
            ids = request.data.get("id")

            slot_per_week = int(settings["slot_per_week"])
            activities = (
                TtActivity.objects.filter(id__in=ids)
                .select_related("week_pattern", "activity_template")
                .prefetch_related(
                    "week",
                    "week_pattern__week",
                    "staff",
                    "location"
                )
            )
            activity_name = ",".join([a.name for a in activities.all()])

            staff_with_affected_week = {}
            location_with_affected_week = {}
            student_set_with_affected_week = {}

            staff_ids = set()
            location_ids = set()
            student_set_ids = set()
            parent_ids = set()
            reschedule_resource_map = False
            activity_week_objs = []
            parent_week_ids = {}
            for activity in activities:
                if activity.week_pattern:
                    current_week_ids = {aw.id for aw in activity.week_pattern.week.all()}
                else:
                    current_week_ids = {aw.id for aw in activity.week.all()}

                # if the activity is scheduled, only need remove related resource_map
                if activity.scheduled:
                    reschedule_resource_map = True
                    # this part only get for deleted activity staff/location/student_set to decide which want to recalculate, will have another to check in parent site
                    for staff in activity.staff.all():
                        if staff.id not in staff_with_affected_week:
                            staff_with_affected_week[staff.id] = set()
                        staff_with_affected_week[staff.id].update(current_week_ids)
                        staff_ids.add(staff.id)

                    for location in activity.location.all():
                        if location.id not in location_with_affected_week:
                            location_with_affected_week[location.id] = set()
                        location_with_affected_week[location.id].update(current_week_ids)
                        location_ids.add(location.id)

            redis_data = {
                "delete": {},
                "update": {}
            }
            redis_activity_table = "activity"

            # need delete activity first only can recalculate the resource_map
            TtActivity.objects.filter(id__in=ids).delete()

            if activity_week_objs:
                TtActivityWeek.objects.bulk_create(activity_week_objs)

            # have this value means need update for resource map
            if reschedule_resource_map:
                # Filter out entries with empty sets to prevent passing {id: []} to the function
                staff_with_affected_week = {k: v for k, v in staff_with_affected_week.items() if v}
                location_with_affected_week = {k: v for k, v in location_with_affected_week.items() if v}
                redis_data = helper_recalculate_and_get_redis_resource_map(
                    redis_data=redis_data,
                    staff_with_affected_week=staff_with_affected_week,
                    location_with_affected_week=location_with_affected_week,
                    slot_per_week=slot_per_week,
                )
            # delete activity from redis
            for id in ids:
                redis_data["delete"].setdefault(redis_activity_table, []).append(id)

            bulk_sync_to_redis(redis_client,redis_data)

            #kafka
            method = "booking_delete"
            kafka_topic = self.kafka_config['MICROSERVICES_TT_TOPIC']
            if kafka_topic:
                kafka_request_data = {
                    "session_id": request.user.name,
                    "activity_ids": ids
                }
                send_request(kafka_topic, kafka_request_data, None, method)

            audit_trail = AuditTrail.objects.create(
                user_id=request.user.id,
                type=self.audit_type,
                ip_address=get_ip(request)
            )
            remark_param = {
                "name": activity_name
            }

            # call to a function for insert details, so in future if want change to used worker, can modify in function only
            AuditTrailDetails.custom_insert(
                audit_trail=audit_trail,
                action=action,
                remark_param=remark_param,
            )
            response = {

            }

            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)