from datetime import timedelta

from rest_framework import serializers

from apps.base.models import UserNotificationPolicy
from apps.base.models.user_notification_policy import NotificationChannelAPIOptions
from apps.user_management.models import User
from common.api_helpers.custom_fields import DurationSecondsField, OrganizationFilteredPrimaryKeyRelatedField
from common.api_helpers.exceptions import Forbidden
from common.api_helpers.mixins import EagerLoadingMixin


# This serializer should not be user directly
class UserNotificationPolicyBaseSerializer(EagerLoadingMixin, serializers.ModelSerializer):
    id = serializers.CharField(read_only=True, source="public_primary_key")
    notify_by = serializers.ChoiceField(
        read_only=False,
        required=False,
        default=UserNotificationPolicy.NotificationChannel.SLACK,
        choices=NotificationChannelAPIOptions.AVAILABLE_FOR_USE,
    )
    step = serializers.ChoiceField(
        read_only=False,
        required=False,
        default=UserNotificationPolicy.Step.NOTIFY,
        choices=UserNotificationPolicy.Step.choices,
    )
    wait_delay = DurationSecondsField(
        required=False,
        allow_null=True,
        min_value=timedelta(minutes=1),
        max_value=timedelta(hours=24),
    )

    SELECT_RELATED = [
        "user",
    ]

    class Meta:
        model = UserNotificationPolicy
        fields = ["id", "step", "notify_by", "wait_delay", "important", "user"]

        # Field "order" is not consumed by the plugin frontend, but is used by the mobile app
        # TODO: remove this field when the mobile app is updated
        fields += ["order"]
        read_only_fields = ["order"]

    def to_internal_value(self, data):
        data = self._notify_by_to_internal_value(data)
        return super().to_internal_value(data)

    def to_representation(self, instance):
        result = super().to_representation(instance)
        result = self._notify_by_to_representation(instance, result)
        return result

    #  _notify_by_to_internal_value and _notify_by_to_representation are exists because of in EscalationPolicy model
    #  notify_by field has default value NotificationChannel.SLACK and not nullable
    #  We don't want any notify_by value in response if step != Step.NOTIFY
    def _notify_by_to_internal_value(self, data):
        if not data.get("notify_by", None):
            data["notify_by"] = UserNotificationPolicy.NotificationChannel.SLACK
        return data

    def _notify_by_to_representation(self, instance, result):
        if instance.step != UserNotificationPolicy.Step.NOTIFY:
            result["notify_by"] = None
        return result


class UserNotificationPolicySerializer(UserNotificationPolicyBaseSerializer):
    user = OrganizationFilteredPrimaryKeyRelatedField(
        queryset=User.objects,
        required=False,
        allow_null=True,
        many=False,
        display_func=lambda instance: instance.username,
    )
    notify_by = serializers.ChoiceField(
        choices=NotificationChannelAPIOptions.AVAILABLE_FOR_USE,
        default=NotificationChannelAPIOptions.DEFAULT_NOTIFICATION_CHANNEL,
    )

    def create(self, validated_data):
        user = validated_data.get("user") or self.context["request"].user

        if not user.self_or_has_user_settings_admin_permission(
            user_to_check=self.context["request"].user, organization=self.context["request"].auth.organization
        ):
            raise Forbidden()
        return UserNotificationPolicy.objects.create(**validated_data)


class UserNotificationPolicyUpdateSerializer(UserNotificationPolicyBaseSerializer):
    user = OrganizationFilteredPrimaryKeyRelatedField(
        many=False,
        read_only=True,
        display_func=lambda instance: instance.username,
    )

    class Meta(UserNotificationPolicyBaseSerializer.Meta):
        read_only_fields = UserNotificationPolicyBaseSerializer.Meta.read_only_fields + ["user", "important"]

    def update(self, instance, validated_data):
        if not instance.user.self_or_has_user_settings_admin_permission(
            user_to_check=self.context["request"].user, organization=self.context["request"].user.organization
        ):
            raise Forbidden()
        if validated_data.get("step") == UserNotificationPolicy.Step.WAIT and not validated_data.get("wait_delay"):
            validated_data["wait_delay"] = UserNotificationPolicy.FIVE_MINUTES
        return super().update(instance, validated_data)
