"""
store/serializers.py
────────────────────
DRF serializers for the Tai AfroPress book store.

Public-facing read serializers are kept lean (no sensitive fields).
Write serializers validate business rules before data reaches the view.
"""

from decimal import Decimal

from rest_framework import serializers

from catalog_app.models import Book
from .models import (
    Cart, CartItem,
    Order, OrderItem,
    DeliveryArea, PickupLocation,
    Coupon,
    PaybillSettings,
)


# ---------------------------------------------------------------------------
# Book mini-card (embedded in cart / order responses)
# ---------------------------------------------------------------------------

class BookMiniSerializer(serializers.ModelSerializer):
    author_name = serializers.CharField(source="author.name", read_only=True, default=None)
    cover_url = serializers.SerializerMethodField()
    price = serializers.SerializerMethodField()

    class Meta:
        model = Book
        fields = ["id", "title", "author_name", "cover_url", "price", "stock"]

    def get_cover_url(self, obj: Book) -> str | None:
        request = self.context.get("request")
        if obj.cover and request:
            return request.build_absolute_uri(obj.cover.url)
        return None

    def get_price(self, obj: Book):
        return obj.effective_price


# ---------------------------------------------------------------------------
# Cart
# ---------------------------------------------------------------------------

class CartItemSerializer(serializers.ModelSerializer):
    book = BookMiniSerializer(read_only=True)
    # Write-only field so clients send book_id; the response embeds the full book object
    book_id = serializers.PrimaryKeyRelatedField(
        queryset=Book.objects.filter(is_listed=True),
        write_only=True,
        source="book",
    )
    unit_price = serializers.DecimalField(max_digits=10, decimal_places=2, read_only=True)
    line_total = serializers.DecimalField(max_digits=12, decimal_places=2, read_only=True)

    class Meta:
        model = CartItem
        fields = ["id", "book", "book_id", "quantity", "unit_price", "line_total", "added_at"]

    def validate_quantity(self, value: int) -> int:
        if value < 1:
            raise serializers.ValidationError("Quantity must be at least 1.")
        if value > 50:
            raise serializers.ValidationError("Cannot add more than 50 of a single title.")
        return value


class CartSerializer(serializers.ModelSerializer):
    items = CartItemSerializer(many=True, read_only=True)
    total = serializers.DecimalField(max_digits=12, decimal_places=2, read_only=True)
    item_count = serializers.IntegerField(read_only=True)

    class Meta:
        model = Cart
        fields = ["id", "items", "total", "item_count", "updated_at"]


# ---------------------------------------------------------------------------
# Delivery options
# ---------------------------------------------------------------------------

class DeliveryAreaSerializer(serializers.ModelSerializer):
    # Alias model fields to match the frontend DELIVERY_ZONES shape:
    #   county → region,  delivery_fee → price
    region = serializers.CharField(source="county", read_only=True)
    price = serializers.DecimalField(source="delivery_fee", max_digits=8, decimal_places=2, read_only=True)

    class Meta:
        model = DeliveryArea
        fields = ["id", "slug", "name", "region", "price", "estimated_days"]


class PickupLocationSerializer(serializers.ModelSerializer):
    class Meta:
        model = PickupLocation
        fields = ["id", "name", "address", "county", "phone", "opening_hours"]


# ---------------------------------------------------------------------------
# Order (read)
# ---------------------------------------------------------------------------

class OrderItemSerializer(serializers.ModelSerializer):
    book_title = serializers.CharField(source="book.title", read_only=True)

    class Meta:
        model = OrderItem
        fields = [
            "id", "book_title", "book_snapshot",
            "unit_price", "quantity", "total_price",
        ]


class OrderSerializer(serializers.ModelSerializer):
    items = OrderItemSerializer(many=True, read_only=True)
    status_display = serializers.CharField(source="get_status_display", read_only=True)
    delivery_type_display = serializers.CharField(
        source="get_delivery_type_display", read_only=True
    )
    delivery_area = serializers.SlugRelatedField(slug_field="name", read_only=True)
    pickup_location = serializers.SlugRelatedField(slug_field="name", read_only=True)

    class Meta:
        model = Order
        fields = [
            "order_reference", "status", "status_display",
            "total_amount", "delivery_fee", "discount_amount", "final_amount",
            "delivery_type", "delivery_type_display",
            "delivery_area", "delivery_address", "pickup_location",
            "customer_name", "customer_email", "phone_number",
            "mpesa_receipt_number", "payment_confirmed_at",
            "paybill_confirmation_code", "paybill_proof_submitted_at",
            "notes", "items", "created_at", "updated_at",
        ]
        # All fields are read-only; mutations go through CheckoutSerializer
        read_only_fields = fields


# ---------------------------------------------------------------------------
# Checkout (write)
# ---------------------------------------------------------------------------

class CheckoutItemInputSerializer(serializers.Serializer):
    book_id = serializers.PrimaryKeyRelatedField(
        queryset=Book.objects.filter(is_listed=True),
        source="book",
    )
    quantity = serializers.IntegerField(min_value=1, max_value=50)


class CheckoutSerializer(serializers.Serializer):
    """
    Validates the POST /api/checkout/ request body.

    Required fields differ by delivery_type:
      delivery  → delivery_address (delivery_area_id optional if area is free-text)
      pickup    → pickup_location_id

    items: optional list of {book_id, quantity}. apps/store's cart lives
    entirely client-side and always sends this — the view treats it as
    authoritative and syncs the session cart to match it exactly, rather
    than depending on third-party session cookies having been populated by
    a prior POST /api/cart/add/ call (which this frontend never makes).
    """
    customer_name = serializers.CharField(max_length=255)
    customer_email = serializers.EmailField(required=False, allow_blank=True, default="")
    phone_number = serializers.CharField(max_length=20)

    delivery_type = serializers.ChoiceField(choices=["delivery", "pickup"])

    # Home delivery
    delivery_area_id = serializers.PrimaryKeyRelatedField(
        queryset=DeliveryArea.objects.filter(is_active=True),
        required=False, allow_null=True, default=None,
    )
    delivery_address = serializers.DictField(
        child=serializers.CharField(allow_blank=True),
        required=False, allow_null=True, default=None,
        help_text=(
            "JSON object with keys: street, area, city, county, postal_code."
            " Required when delivery_type=delivery."
        ),
    )

    # Pickup
    pickup_location_id = serializers.PrimaryKeyRelatedField(
        queryset=PickupLocation.objects.filter(is_active=True),
        required=False, allow_null=True, default=None,
    )

    coupon_code = serializers.CharField(
        max_length=50, required=False, allow_blank=True, default=""
    )
    notes = serializers.CharField(required=False, allow_blank=True, default="")

    # Cart items sent directly from the frontend — authoritative when present
    items = CheckoutItemInputSerializer(many=True, required=False, default=list)

    def validate(self, data: dict) -> dict:
        if data["delivery_type"] == "delivery":
            if not data.get("delivery_address"):
                raise serializers.ValidationError(
                    {"delivery_address": "A delivery address is required for home delivery."}
                )
        elif data["delivery_type"] == "pickup":
            if not data.get("pickup_location_id"):
                raise serializers.ValidationError(
                    {"pickup_location_id": "Required for pickup orders."}
                )
        return data


# ---------------------------------------------------------------------------
# M-Pesa STK Push (write)
# ---------------------------------------------------------------------------

class STKPushSerializer(serializers.Serializer):
    """
    Validates the POST /api/payments/mpesa/stk-push/ request body.

    phone_number accepts Kenyan formats: 07XX, +2547XX, 2547XX.
    Normalisation to 254XXXXXXXXX happens in MpesaService.normalize_phone().
    """
    order_reference = serializers.CharField(max_length=30)
    phone_number = serializers.CharField(max_length=20)

    def validate_phone_number(self, value: str) -> str:
        cleaned = value.strip().replace(" ", "").replace("-", "")
        if not cleaned:
            raise serializers.ValidationError("Phone number is required.")
        return cleaned


# ---------------------------------------------------------------------------
# PaybillSettings (read)
# ---------------------------------------------------------------------------

class PaybillSettingsSerializer(serializers.ModelSerializer):
    class Meta:
        model = PaybillSettings
        fields = ["business_number", "account_number", "updated_at"]
