"""Basket schemas for UFaaS application."""

import uuid
from datetime import datetime
from decimal import Decimal
from enum import StrEnum
from typing import Any, Literal

import httpx
from fastapi_mongo_base.schemas import TenantScopedEntitySchema
from fastapi_mongo_base.utils.bsontools import decimal_amount
from pydantic import BaseModel, Field, field_validator

from ufaas.apps.saas.schemas import Bundle
from ufaas.schemas import Currency


class DiscountSchema(BaseModel):
    """Schema for discount information."""

    code: str
    user_id: uuid.UUID
    discount: Decimal = Field(
        default=Decimal(0), gt=0, description="Discount amount"
    )

    @field_validator("discount", mode="before")
    def validate_discount(self, value: Decimal) -> Decimal:
        return decimal_amount(value)


class ItemType(StrEnum):
    """Enumeration for basket item types."""

    saas_package = "saas_package"
    retail_product = "retail_product"


class BasketItemCreateSchema(BaseModel):
    """Schema for creating basket items."""

    product_url: str
    currency: Currency = Currency.IRR
    quantity: Decimal = Decimal(1)
    # extra_data: dict | None = None

    def from_allowed_domain(self) -> bool:
        """Check if the item is from an allowed domain.

        Returns:
            Always returns True
        """
        return True

    async def get_basket_item(self) -> "BasketItemSchema":
        """Fetch basket item data from product URL.

        Returns:
            BasketItemSchema instance with fetched data
        """
        async with httpx.AsyncClient() as client:
            response = await client.get(
                self.product_url, headers={"Accept-Encoding": "identity"}
            )
            response.raise_for_status()
            data: dict = response.json()
        data.update(self.model_dump(exclude_none=True))
        return BasketItemSchema(**data)


class BasketItemSchema(BasketItemCreateSchema):
    """Schema for basket items with additional fields."""

    uid: uuid.UUID = Field(default_factory=uuid.uuid4)
    name: str
    description: str | None = None
    unit_price: Decimal

    # Item type to distinguish between SaaS and e-commerce
    item_type: ItemType = (
        ItemType.retail_product
    )  # Default to e-commerce product

    webhook_url: str | None = None
    reserve_url: str | None = None
    validation_url: str | None = None

    revenue_share_id: uuid.UUID | None = None
    tax_id: str | None = None
    merchant: str | None = None
    discount: DiscountSchema | None = None

    # SaaS-specific fields
    plan_duration: int | None = None  # Only for SaaS packages
    bundles: list[Bundle] | None = None  # Optional field for SaaS packages

    variant: dict[str, str] | None = None

    # Optional additional data field for future extensions or custom data
    meta_data: dict | None = None

    @property
    def price(self) -> Decimal:
        """Calculate the final price after discount.

        Returns:
            Final price after applying discount
        """
        price = self.unit_price * self.quantity
        if self.discount:
            price -= self.discount.discount
        return price

    def exchange_fee(self, currency: Currency) -> int:
        """Calculate exchange fee for currency conversion.

        Args:
            currency: Target currency

        Returns:
            Exchange fee amount

        Raises:
            NotImplementedError: When currency exchange is not supported
        """
        if self.currency != currency:
            # TODO: Implement currency exchange
            raise NotImplementedError("Currency exchange not implemented")
        return 1

    @field_validator("unit_price", mode="before")
    def validate_price(self, value: Decimal) -> Decimal:
        return decimal_amount(value)

    @field_validator("quantity", mode="before")
    def validate_quantity(self, value: Decimal) -> Decimal:
        return decimal_amount(value)

    async def validate_product(self) -> bool:
        """Validate product information and stock availability.

        Returns:
            True if product is valid and available, False otherwise

        Raises:
            ValueError: When validation URL is not set
        """
        if self.validation_url is None:
            raise ValueError("Validation URL is not set")

        async with httpx.AsyncClient() as client:
            response = await client.get(self.validation_url)
            response.raise_for_status()
            validation_data = response.json()

        if validation_data.get("price") != self.unit_price:
            return False

        if validation_data.get("stock_quantity") is None:
            return True

        return validation_data.get("stock_quantity") >= self.quantity

    async def reserve_product(self) -> dict | None:
        """Reserve the product if reserve URL is available.

        Returns:
            Reservation response data or None if no reserve URL
        """
        if self.reserve_url is None:
            return

        async with httpx.AsyncClient() as client:
            response = await client.post(
                self.reserve_url, json=self.model_dump()
            )
            response.raise_for_status()
            return response.json()

    async def webhook_product(self) -> dict | None:
        """Send webhook notification for the product.

        Returns:
            Webhook response data or None if no webhook URL
        """
        if self.webhook_url is None:
            return

        async with httpx.AsyncClient() as client:
            response = await client.post(
                self.webhook_url, json=self.model_dump()
            )
            response.raise_for_status()
            return response.json()


class BasketItemChangeSchema(BaseModel):
    """Schema for changing basket item quantities."""

    quantity_change: Decimal = Decimal(1)


class BasketStatusEnum(StrEnum):
    """Enumeration for basket status values."""

    active = "active"
    locked = "locked"
    reserved = "reserved"
    paid = "paid"
    cancelled = "cancelled"
    expired = "expired"


class BasketDataSchema(TenantScopedEntitySchema):
    """Schema for basket data with tenant scope."""

    status: BasketStatusEnum = Field(
        default=BasketStatusEnum.active, description="Status of the basket"
    )
    callback_url: str | None = Field(description="Callback URL for the basket")

    currency: Currency = Currency.IRR

    checkout_at: datetime | None = None
    payment_detail_url: str | None = None
    invoice_id: uuid.UUID | None = None

    discount: DiscountSchema | None = None

    @property
    def is_modifiable(self) -> bool:
        """Check if basket can be modified.

        Returns:
            True if basket is active and modifiable
        """
        return self.status == "active"


class BasketDetailSchema(BasketDataSchema):
    """Schema for detailed basket information including items."""

    items: list[BasketItemSchema] = Field(default_factory=list)
    subtotal: Decimal = Field(description="Total amount of the basket")
    amount: Decimal = Field(
        description="Total amount of the basket after discount"
    )

    @field_validator("items", mode="before")
    def validate_items(
        self, value: list[BasketItemSchema] | dict[str, BasketItemSchema]
    ) -> list[BasketItemSchema]:
        if isinstance(value, dict):
            return list(value.values())
        return value

    @field_validator("subtotal", mode="before")
    def validate_subtotal(self, value: Decimal) -> Decimal:
        return decimal_amount(value)

    @field_validator("amount", mode="before")
    def validate_amount(self, value: Decimal) -> Decimal:
        return decimal_amount(value)


class BasketCreateSchema(BaseModel):
    """Schema for creating new baskets."""

    callback_url: str | None = None
    meta_data: dict[str, Any] | None = None


class VoucherSchema(BaseModel):
    """Schema for voucher information."""

    code: str


class BasketUpdateSchema(BaseModel):
    """Schema for updating basket information."""

    status: (
        Literal["active", "inactive", "paid", "reserve", "cancel"] | None
    ) = None
    items: list[BasketItemSchema] | None = None

    payment_detail_url: str | None = None
    meta_data: dict[str, Any] | None = None

    checkout_at: datetime | None = None
    invoice_id: uuid.UUID | None = None

    voucher: VoucherSchema | None = None
