from contextlib import suppress
from typing import Collection, Any

from rest_framework.fields import JSONField, SerializerMethodField
from rest_framework.relations import PrimaryKeyRelatedField
from rest_framework.serializers import ModelSerializer

from core.models import (
    Attribute,
    AttributeGroup,
    AttributeValue,
    Brand,
    Category,
    Feedback,
    Order,
    OrderProduct,
    Product,
    ProductImage,
    ProductTag,
    PromoCode,
    Promotion,
    Stock,
    Vendor,
    Wishlist,
)
from core.serializers.utility import AddressSerializer


class AttributeGroupSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    parent = PrimaryKeyRelatedField(read_only=True)  # type: ignore [assignment, var-annotated]
    children = PrimaryKeyRelatedField(many=True, read_only=True)  # type: ignore [assignment, var-annotated]

    class Meta:
        model = AttributeGroup
        fields = [
            "uuid",
            "name",
            "parent",
            "children",
        ]


class CategorySimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    children = SerializerMethodField()
    image = SerializerMethodField()

    class Meta:
        model = Category
        fields = [
            "uuid",
            "name",
            "image",
            "slug",
            "children",
        ]

    def get_image(self, obj: Category) -> str | None:
        with suppress(ValueError):
            return str(obj.image.url)
        return None

    def get_children(self, obj: Category) -> Collection[Any]:
        request = self.context.get("request")
        if request is not None and request.user.has_perm("view_category"):
            children = obj.children.all()
        else:
            children = obj.children.filter(is_active=True)

        return CategorySimpleSerializer(children, many=True, context=self.context).data if obj.children.exists() else []


class BrandSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    small_logo = SerializerMethodField()
    big_logo = SerializerMethodField()

    class Meta:
        model = Brand
        fields = [
            "uuid",
            "name",
            "small_logo",
            "big_logo",
        ]

    def get_small_logo(self, obj: Brand) -> str | None:
        with suppress(ValueError):
            return str(obj.small_logo.url)
        return None

    def get_big_logo(self, obj: Brand) -> str | None:
        with suppress(ValueError):
            return str(obj.big_logo.url)
        return None


class ProductTagSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    class Meta:
        model = ProductTag
        fields = [
            "uuid",
            "tag_name",
            "name",
        ]


class ProductImageSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    product: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)  # type: ignore [type-arg]

    class Meta:
        model = ProductImage
        fields = [
            "uuid",
            "alt",
            "image",
            "priority",
            "product",
        ]


class AttributeSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    group = AttributeGroupSimpleSerializer(read_only=True)

    class Meta:
        model = Attribute
        fields = [
            "uuid",
            "name",
            "value_type",
            "group",
        ]


class AttributeValueSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    attribute = AttributeSimpleSerializer(read_only=True)
    product: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)  # type: ignore [type-arg]

    class Meta:
        model = AttributeValue
        fields = [
            "uuid",
            "value",
            "attribute",
            "product",
        ]


class ProductSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    brand = BrandSimpleSerializer(read_only=True)
    category = CategorySimpleSerializer(read_only=True)
    tags = ProductTagSimpleSerializer(many=True, read_only=True)
    images = ProductImageSimpleSerializer(many=True, read_only=True)

    attributes = AttributeValueSimpleSerializer(many=True, read_only=True)

    rating = SerializerMethodField()
    price = SerializerMethodField()
    quantity = SerializerMethodField()
    feedbacks_count = SerializerMethodField()
    personal_orders_only = SerializerMethodField()

    class Meta:
        model = Product
        fields = [
            "uuid",
            "name",
            "sku",
            "is_digital",
            "slug",
            "description",
            "partnumber",
            "brand",
            "feedbacks_count",
            "personal_orders_only",
            "category",
            "tags",
            "images",
            "attributes",
            "rating",
            "price",
            "quantity",
        ]

    def get_rating(self, obj: Product) -> float:
        return obj.rating

    def get_price(self, obj: Product) -> float:
        return obj.price

    def get_feedbacks_count(self, obj: Product) -> int:
        return obj.feedbacks_count

    def get_quantity(self, obj: Product) -> int:
        return obj.quantity

    def get_personal_orders_only(self, obj: Product) -> bool:
        return obj.personal_orders_only


class VendorSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    class Meta:
        model = Vendor
        fields = [
            "uuid",
            "name",
        ]


class StockSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    vendor = VendorSimpleSerializer(read_only=True)
    product = ProductSimpleSerializer(read_only=True)

    class Meta:
        model = Stock
        fields = [
            "uuid",
            "price",
            "purchase_price",
            "quantity",
            "sku",
            "vendor",
            "product",
        ]


class PromoCodeSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    class Meta:
        model = PromoCode
        fields = [
            "uuid",
            "code",
        ]


class PromotionSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    products = ProductSimpleSerializer(many=True, read_only=True)

    class Meta:
        model = Promotion
        fields = [
            "uuid",
            "name",
            "discount_percent",
            "products",
        ]


class WishlistSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    user: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)  # type: ignore [type-arg]
    products = ProductSimpleSerializer(many=True, read_only=True)

    class Meta:
        model = Wishlist
        fields = [
            "uuid",
            "user",
            "products",
        ]


class FeedbackSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    order_product: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)  # type: ignore [type-arg]

    class Meta:
        model = Feedback
        fields = [
            "uuid",
            "rating",
            "order_product",
        ]


class OrderProductSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    product = ProductSimpleSerializer(read_only=True)

    class Meta:
        model = OrderProduct
        fields = [
            "uuid",
            "product",
            "quantity",
            "buy_price",
            "status",
        ]


class OrderSimpleSerializer(ModelSerializer):  # type: ignore [type-arg]
    user: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)  # type: ignore [type-arg]
    promo_code = PromoCodeSimpleSerializer(read_only=True)
    order_products = OrderProductSimpleSerializer(many=True, read_only=True)
    billing_address = AddressSerializer(read_only=True, required=False)
    shipping_address = AddressSerializer(read_only=True, required=False)
    attributes = JSONField(required=False)
    total_price = SerializerMethodField(read_only=True)

    class Meta:
        model = Order
        fields = [
            "uuid",
            "human_readable_id",
            "billing_address",
            "shipping_address",
            "attributes",
            "status",
            "user",
            "total_price",
            "promo_code",
            "order_products",
            "buy_time",
            "created",
            "modified",
        ]

    def get_total_price(self, obj: Order) -> float:
        return obj.total_price
