from datetime import datetime
from typing import Any, Dict, List, Optional

from bson import ObjectId
from fastapi import APIRouter, Depends, File, Form, HTTPException, Path, Query, UploadFile, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from pydantic import BaseModel, Field

from app.db.db import get_col
from app.models.category import Category
from app.models.product import Product
from app.models.productVarient import ProductVariant
from app.utility.security import verify_jwt
from app.utility.cloudinary_utils import (
    delete_image_from_cloudinary,
    extract_public_id_from_url,
    upload_image_to_cloudinary,
)


router = APIRouter(prefix="/products", tags=["products"])
auth_scheme = HTTPBearer(auto_error=False)


class ProductCreate(Product):
    variants: List[ProductVariant] = Field(default_factory=list)


class ProductUpdate(BaseModel):
    p_name: Optional[str] = None
    p_short_description: Optional[str] = None
    p_detailed_description: Optional[str] = None
    p_category: Optional[str] = None
    p_sub_category: Optional[str] = None
    p_Product_Type: Optional[str] = None
    p_url: Optional[str] = None
    p_meta_Title: Optional[str] = None
    p_meta_Description: Optional[str] = None
    p_meta_Keywords: Optional[List[str]] = None
    p_schema_Markup: Optional[str] = None
    p_free_delivery: Optional[bool] = None
    p_is_50_voucher: Optional[bool] = None
    p_is_100_voucher: Optional[bool] = None
    p_images: Optional[List[str]] = None


class CategoryCreate(Category):
    pass


class CategoryUpdate(BaseModel):
    category_name: Optional[str] = None
    category_image: Optional[str] = None
    status: Optional[bool] = None

from bson import ObjectId
from fastapi import HTTPException

def list_products_with_variants(category: str, skip: int, limit: int):
    products_col = get_col("products")
    variants_col = get_col("product_variants")

    # Step 1: Get products by category
    cursor = products_col.find({"p_category": category}).skip(skip).limit(limit)
    products = list(cursor)

    if not products:
        return []

    results = []

    for product in products:
        product_id = product["_id"]

        # Step 2: Get variants for each product
        variants = list(
            variants_col.find({"product": ObjectId(product_id)})
        )

        # Step 3: Convert ObjectId to string
        product["_id"] = str(product["_id"])
        for v in variants:
            v["_id"] = str(v["_id"])
            v["product"] = str(v["product"])

        # Step 4: Add variants inside product response
        product["variants"] = variants

        results.append(product)

    return results


def _ensure_object_id(id_str: str) -> ObjectId:
    try:
        return ObjectId(id_str)
    except Exception as exc:  # pragma: no cover - defensive
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid identifier") from exc


def _serialize_datetime(value: Any) -> Any:
    if isinstance(value, datetime):
        return value.isoformat()
    return value


def _serialize_product(doc: Dict[str, Any], variants: Optional[List[Dict[str, Any]]] = None) -> Dict[str, Any]:
    result = {
        "id": str(doc["_id"]),
        "p_name": doc["p_name"],
        "p_short_description": doc["p_short_description"],
        "p_detailed_description": doc["p_detailed_description"],
        "p_category": doc["p_category"],
        "p_sub_category": doc["p_sub_category"],
        "p_Product_Type": doc.get("p_Product_Type", "product"),
        "p_url": doc["p_url"],
        "p_meta_Title": doc.get("p_meta_Title"),
        "p_meta_Description": doc.get("p_meta_Description"),
        "p_meta_Keywords": doc.get("p_meta_Keywords", []),
        "p_schema_Markup": doc.get("p_schema_Markup"),
        "p_free_delivery": bool(doc.get("p_free_delivery", False)),
        "p_is_50_voucher": bool(doc.get("p_is_50_voucher", False)),
        "p_is_100_voucher": bool(doc.get("p_is_100_voucher", False)),
        "p_images": doc.get("p_images", []),
        "createdAt": _serialize_datetime(doc.get("createdAt")),
        "updatedAt": _serialize_datetime(doc.get("updatedAt")),
    }
    if variants is not None:
        result["variants"] = variants
    return result


def _require_admin(credentials: HTTPAuthorizationCredentials = Depends(auth_scheme)) -> Dict[str, Any]:
    if not credentials:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated")
    ok, payload = verify_jwt(credentials.credentials)
    if not ok or not payload:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
    if payload.get("role") != "admin":
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required")
    admins = get_col("admins")
    admin_user = admins.find_one({"_id": _ensure_object_id(payload["sub"])})
    if not admin_user:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Admin not found")
    return admin_user


def _serialize_variant(doc: Dict[str, Any]) -> Dict[str, Any]:
    return {
        "id": str(doc["_id"]),
        "productId": str(doc["product"]),
        "variant_Type": doc["variant_Type"],
        "variant_Values": doc["variant_Values"],
        "offer_price": doc.get("offer_price"),
        "offer_percentage": doc.get("offer_percentage"),
        "price": doc["price"],
        "sku": doc.get("sku"),
        "stock": doc["stock"],
        "total_sold": doc.get("total_sold", 0),
        "variant_url": doc.get("variant_url"),
        "schema_markup": doc.get("schema_markup"),
        "createdAt": _serialize_datetime(doc.get("createdAt")),
        "updatedAt": _serialize_datetime(doc.get("updatedAt"))
    }


def _ensure_category_links(product_doc: Dict[str, Any]) -> None:
    categories = get_col("categories")
    now = datetime.utcnow()
    category_name = product_doc["p_category"]
    product_id_str = str(product_doc["_id"])
    
    # Check if category exists
    existing_category = categories.find_one({"category_name": category_name})
    
    if existing_category:
        # Category exists, just add product to list and update timestamp
        categories.update_one(
            {"category_name": category_name},
            {
                "$addToSet": {"product_list": product_id_str},
                "$set": {"updatedAt": now},
            }
        )
    else:
        # Category doesn't exist, create it with product in list
        categories.insert_one({
            "category_name": category_name,
            "category_image": product_doc.get("category_image"),
            "status": True,
            "product_list": [product_id_str],
            "createdAt": now,
            "updatedAt": now,
        })
    
    if product_doc.get("p_sub_category"):
        sub_categories = get_col("sub_categories")
        sub_category_name = product_doc["p_sub_category"]
        
        # Check if sub-category exists
        existing_sub_category = sub_categories.find_one({
            "category_name": category_name,
            "sub_category_name": sub_category_name
        })
        
        if existing_sub_category:
            # Sub-category exists, just add product to list and update timestamp
            sub_categories.update_one(
                {"category_name": category_name, "sub_category_name": sub_category_name},
                {
                    "$addToSet": {"product_list": product_id_str},
                    "$set": {"updatedAt": now},
                }
            )
        else:
            # Sub-category doesn't exist, create it with product in list
            sub_categories.insert_one({
                "category_name": category_name,
                "sub_category_name": sub_category_name,
                "status": True,
                "product_list": [product_id_str],
                "createdAt": now,
                "updatedAt": now,
            })


def _unlink_from_category(product_doc: Dict[str, Any]) -> None:
    categories = get_col("categories")
    categories.update_one(
        {"category_name": product_doc["p_category"]},
        {"$pull": {"product_list": str(product_doc["_id"])}, "$set": {"updatedAt": datetime.utcnow()}},
    )
    if product_doc.get("p_sub_category"):
        sub_categories = get_col("sub_categories")
        sub_categories.update_one(
            {"category_name": product_doc["p_category"], "sub_category_name": product_doc["p_sub_category"]},
            {"$pull": {"product_list": str(product_doc["_id"])}, "$set": {"updatedAt": datetime.utcnow()}},
        )


@router.post("/create", status_code=status.HTTP_201_CREATED)
async def create_product(
    p_name: str = Form(...),
    p_short_description: str = Form(...),
    p_detailed_description: str = Form(...),
    p_category: str = Form(...),
    p_sub_category: str = Form(...),
    p_url: str = Form(...),
    p_Product_Type: str = Form("product"),
    p_meta_Title: Optional[str] = Form(None),
    p_meta_Description: Optional[str] = Form(None),
    p_meta_Keywords: Optional[str] = Form(None),  # Comma-separated string
    p_schema_Markup: Optional[str] = Form(None),
    p_free_delivery: bool = Form(False),
    p_is_50_voucher: bool = Form(False),
    p_is_100_voucher: bool = Form(False),
    images: List[UploadFile] = File(...),
    # variants: Optional[str] = Form(None),  # JSON string for variants
    _admin=Depends(_require_admin),
):
    import json
    
    try:
        products = get_col("products")
        if products.find_one({"p_url": p_url}):
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Product URL already exists")
        
        # Validate image count: minimum 3, maximum 15
        if not images or len(images) == 0:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="At least 3 images are required"
            )
        if len(images) < 3:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Minimum 3 images required. You provided {len(images)}"
            )
        if len(images) > 15:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Maximum 15 images allowed. You provided {len(images)}"
            )
        
        # Upload images to Cloudinary
        image_urls: List[str] = []
        for idx, image in enumerate(images):
            try:
                image_url = upload_image_to_cloudinary(image, folder="products")
                image_urls.append(image_url)
            except HTTPException:
                raise
            except Exception as e:
                raise HTTPException(
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    detail=f"Failed to upload image {idx + 1}: {str(e)}"
                )
        
        # Parse meta keywords if provided
        meta_keywords_list: Optional[List[str]] = None
        if p_meta_Keywords:
            meta_keywords_list = [kw.strip() for kw in p_meta_Keywords.split(",") if kw.strip()]
        
        now = datetime.utcnow()
        product_payload = {
            "p_name": p_name,
            "p_short_description": p_short_description,
            "p_detailed_description": p_detailed_description,
            "p_category": p_category,
            "p_sub_category": p_sub_category,
            "p_Product_Type": p_Product_Type,
            "p_url": p_url,
            "p_meta_Title": p_meta_Title,
            "p_meta_Description": p_meta_Description,
            "p_meta_Keywords": meta_keywords_list,
            "p_schema_Markup": p_schema_Markup,
            "p_free_delivery": p_free_delivery,
            "p_is_50_voucher": p_is_50_voucher,
            "p_is_100_voucher": p_is_100_voucher,
            "p_images": image_urls,
            "createdAt": now,
            "updatedAt": now,
        }
        res = products.insert_one(product_payload)
        inserted = products.find_one({"_id": res.inserted_id})
        _ensure_category_links(inserted)

        # Parse and insert variants if provided
        variant_docs: List[Dict[str, Any]] = []
        if variants:
            try:
                variants_data = json.loads(variants)
                for variant_data in variants_data:
                    variant_doc = {
                        "product": res.inserted_id,
                        "variant_Type": variant_data.get("variant_Type"),
                        "variant_Values": variant_data.get("variant_Values"),
                        "price": variant_data.get("price"),
                        "offer_price": variant_data.get("offer_price"),
                        "offer_percentage": variant_data.get("offer_percentage"),
                        "sku": variant_data.get("sku"),
                        "stock": variant_data.get("stock"),
                        "total_sold": variant_data.get("total_sold", 0),
                        "variant_url": variant_data.get("variant_url"),
                        "schema_markup": variant_data.get("schema_markup"),
                        "createdAt": now,
                        "updatedAt": now,
                    }
                    variant_docs.append(variant_doc)
            except json.JSONDecodeError:
                pass  # Invalid JSON, skip variants
        
        if variant_docs:
            get_col("product_variants").insert_many(variant_docs)
        variants_result = (
            list(map(_serialize_variant, get_col("product_variants").find({"product": res.inserted_id})))
            if variant_docs
            else []
        )
        return _serialize_product(inserted, variants=variants_result)
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error creating product: {str(e)}"
        )


def _list_products_helper(
    category: Optional[str] = None,
    sub_category: Optional[str] = None,
    limit: int = 50,
    skip: int = 0,
) -> List[Dict[str, Any]]:
    """Helper function to list products with filters."""
    products = get_col("products")
    query: Dict[str, Any] = {}
    if category:
        query["p_category"] = category
    if sub_category:
        query["p_sub_category"] = sub_category
    cursor = products.find(query).skip(skip).limit(limit)
    return [_serialize_product(doc) for doc in cursor]


@router.get("/all", status_code=status.HTTP_200_OK)
def list_products(
    category: Optional[str] = Query(default=None),
    sub_category: Optional[str] = Query(default=None),
    limit: int = Query(default=50, ge=1, le=200),
    skip: int = Query(default=0, ge=0),
):
    return _list_products_helper(category=category, sub_category=sub_category, limit=limit, skip=skip)


@router.get("/{product_id}", status_code=status.HTTP_200_OK)
def get_product(product_id: str = Path(..., description="Product identifier")):
    products = get_col("products")
    oid = _ensure_object_id(product_id)
    doc = products.find_one({"_id": oid})
    if not doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not found")
    variants = list(map(_serialize_variant, get_col("product_variants").find({"product": oid})))
    return _serialize_product(doc, variants=variants)


@router.get("/products/{p_url}", status_code=status.HTTP_200_OK)
def get_product_by_url(p_url: str):
    products = get_col("products")

    # Find product by p_url
    doc = products.find_one({"p_url": p_url})
    if not doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not found")

    # Fetch variants using the same serialization function for consistency
    variants_cursor = get_col("product_variants").find({"product": doc["_id"]})
    variants = list(map(_serialize_variant, variants_cursor))

    # Return final response
    return _serialize_product(doc, variants=variants)


@router.put("/{product_id}", status_code=status.HTTP_200_OK)
async def update_product(
    product_id: str,
    p_name: Optional[str] = Form(None),
    p_short_description: Optional[str] = Form(None),
    p_detailed_description: Optional[str] = Form(None),
    p_category: Optional[str] = Form(None),
    p_sub_category: Optional[str] = Form(None),
    p_url: Optional[str] = Form(None),
    p_Product_Type: Optional[str] = Form(None),
    p_meta_Title: Optional[str] = Form(None),
    p_meta_Description: Optional[str] = Form(None),
    p_meta_Keywords: Optional[str] = Form(None),
    p_schema_Markup: Optional[str] = Form(None),
    p_free_delivery: Optional[bool] = Form(None),
    p_is_50_voucher: Optional[bool] = Form(None),
    p_is_100_voucher: Optional[bool] = Form(None),
    images: Optional[List[UploadFile]] = File(None),
    _admin=Depends(_require_admin),
):
    import json
    
    products = get_col("products")
    oid = _ensure_object_id(product_id)
    doc = products.find_one({"_id": oid})
    if not doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not found")
    
    payload: Dict[str, Any] = {}
    
    if p_name is not None:
        payload["p_name"] = p_name
    if p_short_description is not None:
        payload["p_short_description"] = p_short_description
    if p_detailed_description is not None:
        payload["p_detailed_description"] = p_detailed_description
    if p_category is not None:
        payload["p_category"] = p_category
    if p_sub_category is not None:
        payload["p_sub_category"] = p_sub_category
    if p_url is not None:
        existing = products.find_one({"p_url": p_url, "_id": {"$ne": oid}})
        if existing:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Product URL already exists")
        payload["p_url"] = p_url
    if p_Product_Type is not None:
        payload["p_Product_Type"] = p_Product_Type
    if p_meta_Title is not None:
        payload["p_meta_Title"] = p_meta_Title
    if p_meta_Description is not None:
        payload["p_meta_Description"] = p_meta_Description
    if p_meta_Keywords is not None:
        meta_keywords_list = [kw.strip() for kw in p_meta_Keywords.split(",") if kw.strip()]
        payload["p_meta_Keywords"] = meta_keywords_list
    if p_schema_Markup is not None:
        payload["p_schema_Markup"] = p_schema_Markup
    if p_free_delivery is not None:
        payload["p_free_delivery"] = p_free_delivery
    if p_is_50_voucher is not None:
        payload["p_is_50_voucher"] = p_is_50_voucher
    if p_is_100_voucher is not None:
        payload["p_is_100_voucher"] = p_is_100_voucher
    if images is not None:
        # Delete old images from Cloudinary
        old_images = doc.get("p_images", [])
        for old_image_url in old_images:
            public_id = extract_public_id_from_url(old_image_url)
            if public_id:
                delete_image_from_cloudinary(public_id)
        
        # Upload new images
        image_urls: List[str] = []
        for image in images:
            image_url = upload_image_to_cloudinary(image, folder="products")
            image_urls.append(image_url)
        payload["p_images"] = image_urls
    
    if not payload:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="No fields to update")
    
    payload["updatedAt"] = datetime.utcnow()
    products.update_one({"_id": oid}, {"$set": payload})
    updated = products.find_one({"_id": oid})
    _ensure_category_links(updated)
    variants = list(map(_serialize_variant, get_col("product_variants").find({"product": oid})))
    return _serialize_product(updated, variants=variants)


@router.delete("/{product_id}", status_code=status.HTTP_200_OK)
def delete_product(product_id: str, _admin=Depends(_require_admin)):
    products = get_col("products")
    oid = _ensure_object_id(product_id)
    doc = products.find_one({"_id": oid})
    if not doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not found")
    
    # Delete images from Cloudinary
    images = doc.get("p_images", [])
    for image_url in images:
        public_id = extract_public_id_from_url(image_url)
        if public_id:
            delete_image_from_cloudinary(public_id)
    
    products.delete_one({"_id": oid})
    get_col("product_variants").delete_many({"product": oid})
    _unlink_from_category(doc)
    return {"message": "Product deleted successfully"}


@router.get("/category/{category_name}", status_code=status.HTTP_200_OK)
def get_products_by_category(
    category_name: str,
    skip: int = Query(default=0, ge=0),
    limit: int = Query(default=50, ge=1, le=200),
):
    return list_products_with_variants(category_name, skip, limit)


@router.get("/category/{category_name}/{sub_category}", status_code=status.HTTP_200_OK)
def get_products_by_category_and_sub(
    category_name: str,
    sub_category: str,
    skip: int = Query(default=0, ge=0),
    limit: int = Query(default=50, ge=1, le=200),
):
    return _list_products_helper(category=category_name, sub_category=sub_category, skip=skip, limit=limit)


@router.post("/categories", status_code=status.HTTP_201_CREATED)
async def create_category(
    category_name: str = Form(...),
    category_image: UploadFile = File(...),
    status: bool = Form(True),
    _admin=Depends(_require_admin),
):
    categories = get_col("categories")
    if categories.find_one({"category_name": category_name}):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Category already exists")
    
    # Upload image to Cloudinary
    image_url = upload_image_to_cloudinary(category_image, folder="categories")
    
    now = datetime.utcnow()
    payload = {
        "category_name": category_name,
        "category_image": image_url,
        "status": status,
        "product_list": [],
        "createdAt": now,
        "updatedAt": now,
    }
    res = categories.insert_one(payload)
    payload["_id"] = res.inserted_id
    return {
        "id": str(res.inserted_id),
        "category_name": payload["category_name"],
        "category_image": payload.get("category_image"),
        "status": payload.get("status", True),
        "product_list": payload.get("product_list", []),
        "createdAt": _serialize_datetime(payload.get("createdAt")),
        "updatedAt": _serialize_datetime(payload.get("updatedAt")),
    }


@router.get("/categories", status_code=status.HTTP_200_OK)
def list_categories():
    categories = get_col("categories")
    return [
        {
            "id": str(doc["_id"]),
            "category_name": doc["category_name"],
            "category_image": doc.get("category_image"),
            "status": doc.get("status", True),
            "product_list": doc.get("product_list", []),
            "createdAt": _serialize_datetime(doc.get("createdAt")),
            "updatedAt": _serialize_datetime(doc.get("updatedAt")),
        }
        for doc in categories.find()
    ]


@router.put("/categories/{category_id}", status_code=status.HTTP_200_OK)
async def update_category(
    category_id: str,
    category_name: Optional[str] = Form(None),
    category_image: Optional[UploadFile] = File(None),
    status: Optional[bool] = Form(None),
    _admin=Depends(_require_admin),
):
    categories = get_col("categories")
    oid = _ensure_object_id(category_id)
    doc = categories.find_one({"_id": oid})
    if not doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Category not found")
    
    payload: Dict[str, Any] = {}
    
    if category_name is not None:
        existing = categories.find_one({"category_name": category_name, "_id": {"$ne": oid}})
        if existing:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Category name already exists")
        payload["category_name"] = category_name
    if status is not None:
        payload["status"] = status
    if category_image is not None:
        # Delete old image from Cloudinary
        old_image_url = doc.get("category_image")
        if old_image_url:
            public_id = extract_public_id_from_url(old_image_url)
            if public_id:
                delete_image_from_cloudinary(public_id)
        
        # Upload new image
        payload["category_image"] = upload_image_to_cloudinary(category_image, folder="categories")
    
    if not payload:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="No fields to update")
    
    payload["updatedAt"] = datetime.utcnow()
    categories.update_one({"_id": oid}, {"$set": payload})
    updated_doc = categories.find_one({"_id": oid})
    return {
        "id": str(updated_doc["_id"]),
        "category_name": updated_doc["category_name"],
        "category_image": updated_doc.get("category_image"),
        "status": updated_doc.get("status", True),
        "product_list": updated_doc.get("product_list", []),
        "createdAt": _serialize_datetime(updated_doc.get("createdAt")),
        "updatedAt": _serialize_datetime(updated_doc.get("updatedAt")),
    }


@router.delete("/categories/{category_id}", status_code=status.HTTP_200_OK)
def delete_category(category_id: str, _admin=Depends(_require_admin)):
    categories = get_col("categories")
    oid = _ensure_object_id(category_id)
    doc = categories.find_one({"_id": oid})
    if not doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Category not found")
    
    # Delete image from Cloudinary
    image_url = doc.get("category_image")
    if image_url:
        public_id = extract_public_id_from_url(image_url)
        if public_id:
            delete_image_from_cloudinary(public_id)
    
    categories.delete_one({"_id": oid})
    return {"message": "Category deleted successfully"}


@router.get("/{p_url}/related", status_code=status.HTTP_200_OK)
def get_related_products(p_url: str):
    products = get_col("products")
    doc = products.find_one({"p_url": p_url})
    if not doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not found")
    
    category = doc["p_category"]
    sub_category = doc.get("p_sub_category")
    
    query: Dict[str, Any] = {"p_category": category}
    if sub_category:
        query["p_sub_category"] = sub_category
    
    cursor = products.find(query).limit(10)
    related_products = []
    for prod in cursor:
        if prod["_id"] != doc["_id"]:
            related_products.append(_serialize_product(prod))
    
    return related_products

@router.get("/hot-deals", status_code=status.HTTP_200_OK)
def get_hot_deal_products():
    products = get_col("products")
    cursor = products.find({
        "$or": [
            {"offer_percentage": {"$gte": 10}},
            {"offer_price": {"$exists": True, "$ne": None}}
        ]
    }).limit(20)
    
    hot_deals = []
    for prod in cursor:
        hot_deals.append(_serialize_product(prod))
    
    return hot_deals

@router.get("/byType/combo", status_code=status.HTTP_200_OK)
def get_combo_products():
    products = get_col("products")
    cursor = products.find({"p_Product_Type": "combo"}).limit(50)
    
    combo_products = []
    for prod in cursor:
        combo_products.append(_serialize_product(prod))
    
    return combo_products
    
@router.get("/category/{category_name}/{sub_category}/{p_url}", status_code=status.HTTP_200_OK)
def get_products_by_category_names(category_name: str, subcategory_name: str, p_url: str):
    categories = get_col("categories")
    doc = categories.find_one({
        "category_name": category_name,
        "subcategory_list.subcategory_name": subcategory_name,
        "subcategory_list.product_list.product_url": p_url
    }, {
        "subcategory_list.$": 1
    })
    if not doc or "subcategory_list" not in doc or not doc["subcategory_list"]:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Category or Subcategory not found")
    
    return _serialize_category(doc)    

@router.get("/product/{p_url}/{variant_url}", status_code=status.HTTP_200_OK)
def get_product_variant_by_urls(p_url: str, variant_url: str):
    products = get_col("products")
    product_doc = products.find_one({"p_url": p_url})
    if not product_doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not found")
    
    variants_col = get_col("product_variants")
    variant_doc = variants_col.find_one({
        "product": product_doc["_id"],
        "variant_url": variant_url
    })
    if not variant_doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Variant not found")
    
    return _serialize_variant(variant_doc)