import logging
from contextlib import suppress
from typing import Any

from drf_spectacular.utils import extend_schema_field
from rest_framework.fields import JSONField, SerializerMethodField
from rest_framework.serializers import ListSerializer, ModelSerializer
from rest_framework_recursive.fields import RecursiveField

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

logger = logging.getLogger(__name__)


class AttributeGroupDetailSerializer(ModelSerializer):
    children = RecursiveField(many=True)

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


class CategoryDetailListSerializer(ListSerializer):
    def to_representation(self, data):  # ty: ignore[invalid-method-override]
        items = list(data)
        with suppress(Exception):
            Category.bulk_prefetch_filterable_attributes(items)
        return super().to_representation(items)


class CategoryDetailSerializer(ModelSerializer):
    children = SerializerMethodField()
    filterable_attributes = SerializerMethodField()

    class Meta:
        model = Category
        list_serializer_class = CategoryDetailListSerializer
        fields = [
            "uuid",
            "name",
            "description",
            "image",
            "markup_percent",
            "filterable_attributes",
            "children",
            "slug",
            "created",
            "modified",
        ]

    def get_filterable_attributes(self, obj: Category) -> list[FilterableAttribute]:
        return obj.filterable_attributes

    @extend_schema_field(CategorySimpleSerializer(many=True))
    def get_children(self, obj: Category) -> list[dict[str, 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)

        if obj.children.exists():
            serializer = CategorySimpleSerializer(
                children, many=True, context=self.context
            )
            return list(serializer.data)  # ty: ignore[invalid-return-type]
        return []


class BrandDetailSerializer(ModelSerializer):
    categories = CategorySimpleSerializer(many=True)

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


class BrandProductDetailSerializer(ModelSerializer):
    class Meta:
        model = Brand
        fields = [
            "uuid",
            "name",
            "created",
            "modified",
            "big_logo",
            "small_logo",
        ]


class ProductTagDetailSerializer(ModelSerializer):
    class Meta:
        model = ProductTag
        fields = [
            "uuid",
            "tag_name",
            "name",
            "created",
            "modified",
        ]


class ProductImageDetailSerializer(ModelSerializer):
    class Meta:
        model = ProductImage
        fields = [
            "uuid",
            "alt",
            "image",
            "priority",
            "created",
            "modified",
        ]


class AttributeDetailSerializer(ModelSerializer):
    categories = CategoryDetailSerializer(many=True)
    group = AttributeGroupDetailSerializer()

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


class AttributeInnerSerializer(ModelSerializer):
    group = AttributeGroupDetailSerializer()

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


class AttributeValueDetailSerializer(ModelSerializer):
    attribute = AttributeInnerSerializer()

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


class VendorDetailSerializer(ModelSerializer):
    class Meta:
        model = Vendor
        fields = [
            "uuid",
            "name",
            "authentication",
            "markup_percent",
            "created",
            "modified",
        ]


class StockDetailSerializer(ModelSerializer):
    vendor = VendorDetailSerializer()

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


class PromoCodeDetailSerializer(ModelSerializer):
    class Meta:
        model = PromoCode
        fields = [
            "uuid",
            "code",
            "discount_amount",
            "discount_percent",
            "start_time",
            "end_time",
            "used_on",
            "created",
            "modified",
        ]


class ProductDetailSerializer(ModelSerializer):
    brand = BrandProductDetailSerializer()
    category = CategorySimpleSerializer()
    tags = ProductTagDetailSerializer(
        many=True,
    )
    images = ProductImageDetailSerializer(
        many=True,
    )
    attributes = AttributeValueDetailSerializer(
        many=True,
    )

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

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

    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_personal_orders_only(self, obj: Product) -> bool:
        return obj.personal_orders_only

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

    def get_discount_price(self, obj: Product) -> float | None:
        return obj.discount_price


class PromotionDetailSerializer(ModelSerializer):
    products = ProductDetailSerializer(
        many=True,
    )

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


class WishlistDetailSerializer(ModelSerializer):
    products = ProductSimpleSerializer(
        many=True,
    )

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


class OrderProductDetailSerializer(ModelSerializer):
    product = ProductDetailSerializer()
    billing_address = AddressSerializer(read_only=True, required=False)
    shipping_address = AddressSerializer(read_only=True, required=False)
    attributes = JSONField(required=False)

    class Meta:
        model = OrderProduct
        fields = [
            "uuid",
            "product",
            "quantity",
            "buy_price",
            "comments",
            "notifications",
            "billing_address",
            "shipping_address",
            "attributes",
            "status",
            "created",
            "modified",
        ]


class FeedbackDetailSerializer(ModelSerializer):
    order_product = OrderProductDetailSerializer()

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


class OrderDetailSerializer(ModelSerializer):
    promo_code = PromoCodeDetailSerializer()
    order_products = OrderProductDetailSerializer(
        many=True,
    )
    total_price = SerializerMethodField(read_only=True)

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

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