from django.core.exceptions import ValidationError
from django.dispatch import receiver
from django.utils.translation import gettext_lazy as _
from rest_framework.reverse import reverse
from wbcore import serializers as wb_serializers
from wbcore.contrib.directory.models import Entry
from wbcore.contrib.directory.serializers import EntryRepresentationSerializer
from wbcore.signals import add_additional_resource
from wbcrm.models import AccountRoleType
from wbcrm.serializers import (
    AccountModelSerializer,
    AccountRepresentationSerializer,
    AccountRoleTypeRepresentationSerializer,
)
from wbportfolio.models import PortfolioRole

from wbcommission.models import Commission, CommissionRule, CommissionType


class CommissionRepresentationSerializer(wb_serializers.RepresentationSerializer):
    class Meta:
        model = Commission
        fields = ("id",)


class CommissionTypeRepresentationSerializer(wb_serializers.RepresentationSerializer):
    _detail = wb_serializers.HyperlinkField(reverse_name="wbcommission:commissiontype-detail")

    class Meta:
        model = CommissionType
        fields = (
            "id",
            "name",
            "key",
            "_detail",
        )


class CommissionTypeModelSerializer(wb_serializers.ModelSerializer):
    class Meta:
        model = CommissionType
        read_only_fields = ("key",)
        fields = (
            "id",
            "name",
            "key",
        )


class CommissionModelSerializer(wb_serializers.ModelSerializer):
    _group_key = wb_serializers.CharField(read_only=True, label="Group Key")
    _account = AccountRepresentationSerializer(source="account")
    crm_recipient = wb_serializers.PrimaryKeyRelatedField(
        queryset=Entry.objects.all(), read_only=lambda view: not view.new_mode, required=False
    )
    _crm_recipient = EntryRepresentationSerializer(source="crm_recipient", read_only=lambda view: not view.new_mode)
    account_role_type_recipient = wb_serializers.PrimaryKeyRelatedField(
        queryset=AccountRoleType.objects.all(), read_only=lambda view: not view.new_mode, required=False
    )
    _account_role_type_recipient = AccountRoleTypeRepresentationSerializer(
        source="account_role_type_recipient", read_only=lambda view: not view.new_mode
    )
    portfolio_role_recipient = wb_serializers.ChoiceField(
        choices=PortfolioRole.RoleType.choices, read_only=lambda view: not view.new_mode, required=False
    )

    _commission_type = CommissionTypeRepresentationSerializer(source="commission_type")
    _exclusion_rule_account_role_type = AccountRoleTypeRepresentationSerializer(
        source="exclusion_rule_account_role_type"
    )

    roles_repr = wb_serializers.CharField(
        read_only=True,
        label="Direct Roles",
        help_text="The direct role this commission rule plays in defining rebate and commission accessibility.",
    )
    percent = wb_serializers.SerializerMethodField(
        read_only=True,
        field_class=wb_serializers.FloatField,
        percent=True,
        help_text="The raw rule percentage. If a simulation product and date are selected, it displays the simulated percentage (in blue).",
    )
    recipient_repr = wb_serializers.SerializerMethodField(
        read_only=True, help_text="The raw recipient group to which this rule applies."
    )
    recipient_details = wb_serializers.SerializerMethodField(
        read_only=True,
        help_text="Preview recipients and their allocated percentages based on the selected product and date (if specified).",
    )
    timespan = wb_serializers.DateRangeField(
        read_only=True, label="Timespan", help_text="The date range at which the rule applies."
    )
    assets_under_management_range = wb_serializers.DecimalRangeField(
        read_only=True, label="AUM Range", help_text="The AUM range at which the rule applies."
    )

    def validate(self, data):
        crm_recipient = data.get("crm_recipient")
        account_role_type_recipient = data.get("account_role_type_recipient")
        portfolio_role_recipient = data.get("portfolio_role_recipient")
        if not crm_recipient and not account_role_type_recipient and not portfolio_role_recipient:
            raise ValidationError(
                {
                    ("crm_recipient"): _("Exactly one recipient field must be set (others left empty)."),
                    ("account_role_type_recipient"): _("Exactly one recipient field must be set (others left empty)."),
                    ("portfolio_role_recipient"): _("Exactly one recipient field must be set (others left empty)."),
                }
            )
        if not self.instance and (view := self.context.get("view")):
            if (
                crm_recipient
                and Commission.objects.filter(
                    account=view.account, commission_type=view.commission_type, crm_recipient=crm_recipient
                ).exists()
            ):
                raise ValidationError({"crm_recipient": "A commission line already exists for this crm recipient."})
            if (
                account_role_type_recipient
                and Commission.objects.filter(
                    account=view.account,
                    commission_type=view.commission_type,
                    account_role_type_recipient=account_role_type_recipient,
                ).exists()
            ):
                raise ValidationError(
                    {"account_role_type_recipient": "A commission line already exists for this account role type."}
                )
            if (
                portfolio_role_recipient
                and Commission.objects.filter(
                    account=view.account,
                    commission_type=view.commission_type,
                    portfolio_role_recipient=portfolio_role_recipient,
                ).exists()
            ):
                raise ValidationError(
                    {"portfolio_role_recipient": "A commission line already exists for this portfolio role type."}
                )
        return data

    def get_recipient_repr(self, obj):
        return obj.get_recipient_repr()

    def get_recipient_details(self, obj):
        res = ""
        if (view := self.context.get("view")) and view.simulation_date and view.simulation_product:
            recipients = obj.get_recipients(view.account, view.simulation_product, view.simulation_date)
            res = ", ".join(map(lambda o: f"{str(o[0])} ({o[1]:.2%})", recipients))
        return res

    def get_percent(self, obj):
        if (view := self.context.get("view")) and (actual_percent := view.actual_percents) is not None:
            return actual_percent.get(obj, 0)
        return None

    def get_unique_together_validators(self):
        return []

    @wb_serializers.register_resource()
    def additional_resources(self, instance, request, user):
        return {
            "rules": reverse("wbcommission:commission-rule-list", args=[instance.id]),
        }

    class Meta:
        model = Commission

        fields = (
            "id",
            "_group_key",
            "order",
            "account",
            "_account",
            "crm_recipient",
            "_crm_recipient",
            "portfolio_role_recipient",
            "account_role_type_recipient",
            "_account_role_type_recipient",
            "_commission_type",
            "net_commission",
            "commission_type",
            "is_hidden",
            "exclusion_rule_account_role_type",
            "_exclusion_rule_account_role_type",
            "roles_repr",
            "recipient_repr",
            "recipient_details",
            "percent",
            "timespan",
            "assets_under_management_range",
            "_additional_resources",
        )


class CommissionRuleCommissionModelSerializer(wb_serializers.ModelSerializer):
    net_commission = wb_serializers.BooleanField(read_only=True, label="Net Commission")

    class Meta:
        model = CommissionRule
        fields = (
            "id",
            "timespan",
            "net_commission",
            "assets_under_management_range",
            "percent",
            "consider_zero_percent_for_exclusion",
            "commission",
        )


@receiver(add_additional_resource, sender=AccountModelSerializer)
def add_commission_resource(sender, serializer, instance, request, user, **kwargs):
    res = {}
    for ct in CommissionType.objects.all():
        res[f"commission-{ct.key}"] = reverse("wbcommission:type-account-commission-list", args=[ct.id, instance.id])
    return res
