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

from bson import ObjectId
from fastapi import APIRouter, HTTPException, Path, status
from pydantic import BaseModel, Field

from app.db.db import get_col
from app.models.ShippFee import ShippingAddress

router = APIRouter(prefix="/orders", tags=["orders"])

orders_col = get_col("orders")
carts_col = get_col("carts")
shipping_fee_col = get_col("shipping_fees")

class ChangeOrderStatusRequest(BaseModel):
    status: str = Field(..., description="Order status", choices=["pending", "shipped", "delivered", "cancelled"])
    trackingNumber: Optional[str] = None

class CreateOrderRequest(BaseModel):
    shippingAddress: ShippingAddress
    paymentMethod: str
    platformFee: Optional[float] = None
    paymentReference: Optional[str] = None
    paymentProvider: Optional[str] = None


def _ensure_object_id(identifier: str) -> ObjectId:
    if not ObjectId.is_valid(identifier):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid identifier")
    return ObjectId(identifier)


def _serialize_order(order_doc: Dict[str, Any]) -> Dict[str, Any]:
    return {
        "id": str(order_doc["_id"]),
        "user": str(order_doc["user"]),
        "orderItems": order_doc.get("orderItems", []),
        "shippingAddress": order_doc.get("shippingAddress"),
        "paymentMethod": order_doc.get("paymentMethod"),
        "paymentResult": order_doc.get("paymentResult"),
        "itemsPrice": order_doc.get("itemsPrice", 0),
        "shippingFee": order_doc.get("shippingFee", 0),
        "platformFee": order_doc.get("platformFee", 0),
        "totalPrice": order_doc.get("totalPrice", 0),
        # "currency": order_doc.get("currency", "INR"),
        "status": order_doc.get("status", "pending"),
        "shippingFeeSource": order_doc.get("shippingFeeSource"),
        "createdAt": order_doc.get("createdAt"),
        "updatedAt": order_doc.get("updatedAt"),
    }


def _prepare_order_items(cart_items: List[Dict[str, Any]]) -> Tuple[List[Dict[str, Any]], float, int]:
    order_items: List[Dict[str, Any]] = []
    subtotal = 0.0
    total_quantity = 0
    for item in cart_items:
        quantity = int(item.get("quantity", 0))
        total_quantity += quantity
        offered_amount = item.get("offer_price")
        unit_price = float(offered_amount if offered_amount is not None else item.get("price", 0))
        subtotal += unit_price * quantity
        order_items.append(
            {
                "product": str(item.get("product")),
                "variantId": str(item.get("variantId")) if item.get("variantId") else None,
                "variantType": item.get("variantType"),
                "variantValue": item.get("variantValue"),
                "quantity": quantity,
                "listedPrice": float(item.get("price", 0)),
                "offer_price": offered_amount,
                "chargedPrice": unit_price,
                "lineTotal": round(unit_price * quantity, 2),
                "addedAt": item.get("createdAt"),
            }
        )
    return order_items, round(subtotal, 2), total_quantity


def _resolve_shipping_fee(state: str, subtotal: float, total_quantity: int) -> Tuple[float, Dict[str, Any], str]:
    fee_doc = shipping_fee_col.find_one({"state": state})
    if not fee_doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Shipping fee not configured for {state}")
    if subtotal >= 500:
        applied_rule = "above_500_fee"
    elif total_quantity > 1:
        applied_rule = "combo_fee"
    else:
        applied_rule = "delivery_fee"
    shipping_amount = float(fee_doc.get(applied_rule, fee_doc.get("delivery_fee", 0)))
    return round(shipping_amount, 2), fee_doc, applied_rule


def _reset_cart(cart_doc: Dict[str, Any]) -> None:
    cart_doc["items"] = []
    cart_doc["totalAmount"] = 0.0
    platform_fee = float(cart_doc.get("platformFee", 0.0))
    shipping = float(cart_doc.get("shippingCharges", 0.0))
    cart_doc["finalAmount"] = platform_fee + shipping
    cart_doc["updatedAt"] = datetime.utcnow()
    payload = {k: v for k, v in cart_doc.items() if k != "_id"}
    carts_col.update_one({"_id": cart_doc["_id"]}, {"$set": payload})


@router.post("/{user_id}", status_code=status.HTTP_201_CREATED)
def create_order(user_id: str, payload: CreateOrderRequest):
    user_oid = _ensure_object_id(user_id)
    cart = carts_col.find_one({"user": user_oid})
    if not cart or not cart.get("items"):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Cart is empty")

    order_items, subtotal, total_quantity = _prepare_order_items(cart.get("items", []))
    shipping_amount, shipping_fee_doc, applied_rule = _resolve_shipping_fee(
        payload.shippingAddress.state, subtotal, total_quantity
    )
    platform_fee = float(
        payload.platformFee if payload.platformFee is not None else cart.get("platformFee", 0.0)
    )
    total_price = round(subtotal + shipping_amount + platform_fee, 2)

    now = datetime.utcnow()
    order_doc = {
        "user": user_oid,
        "orderItems": order_items,
        "shippingAddress": payload.shippingAddress.dict(),
        "paymentMethod": payload.paymentMethod,
        "paymentResult": {
            "amount": total_price,
            "currency": "INR",
            "status": "pending",
            "reference": payload.paymentReference,
            "provider": payload.paymentProvider,
        },
        "itemsPrice": subtotal,
        "shippingFee": shipping_amount,
        "platformFee": platform_fee,
        "totalPrice": total_price,
        "status": "pending",
        "shippingFeeSource": {
            "id": str(shipping_fee_doc["_id"]),
            "state": shipping_fee_doc["state"],
            "appliedRule": applied_rule,
        },
        "createdAt": now,
        "updatedAt": now,
    }

    result = orders_col.insert_one(order_doc)
    created = orders_col.find_one({"_id": result.inserted_id})
    _reset_cart(cart)
    return _serialize_order(created)


@router.get("/{user_id}", status_code=status.HTTP_200_OK)
def list_user_orders(user_id: str):
    user_oid = _ensure_object_id(user_id)
    orders = [_serialize_order(doc) for doc in orders_col.find({"user": user_oid}).sort("createdAt", -1)]
    return orders


@router.get("/admin", status_code=status.HTTP_200_OK)
def list_all_orders():
    orders = [_serialize_order(doc) for doc in orders_col.find().sort("createdAt", -1)]
    return orders


@router.get("/admin/{order_id}", status_code=status.HTTP_200_OK)
def get_order(order_id: str = Path(..., description="Order identifier")):
    order = orders_col.find_one({"_id": _ensure_object_id(order_id)})
    if not order:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Order not found")
    return _serialize_order(order)

@router.post("/admin/{order_id}/change-status", status_code=status.HTTP_200_OK)
def change_order_status(order_id: str, payload: ChangeOrderStatusRequest):
    order = orders_col.find_one({"_id": _ensure_object_id(order_id)})
    if not order:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Order not found")
    order["status"] = payload.status
    order["trackingNumber"] = payload.trackingNumber
    orders_col.update_one({"_id": order["_id"]}, {"$set": order})
    return _serialize_order(order)

@router.post("/admin/{order_id}/cancel", status_code=status.HTTP_200_OK)
def cancel_order(order_id: str):
    order = orders_col.find_one({"_id": _ensure_object_id(order_id)})
    if not order:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Order not found")
    order["status"] = "cancelled"
    orders_col.update_one({"_id": order["_id"]}, {"$set": order})
    return _serialize_order(order)

@router.get("/user/{user_id}/orders/{order_id}", status_code=status.HTTP_200_OK)
def get_user_order(user_id: str, order_id: str):
    user_oid = _ensure_object_id(user_id)
    order = orders_col.find_one({"_id": _ensure_object_id(order_id), "user": user_oid})
    if not order:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Order not found")
    return _serialize_order(order)

@router.get("/user/{user_id}/orders", status_code=status.HTTP_200_OK)
def get_user_orders(user_id: str):
    user_oid = _ensure_object_id(user_id)
    orders = [_serialize_order(doc) for doc in orders_col.find({"user": user_oid}).sort("createdAt", -1)]
    return orders

@router.get("/user/{user_id}/orders/{order_id}/cancel", status_code=status.HTTP_200_OK)
def cancel_user_order(user_id: str, order_id: str):
    user_oid = _ensure_object_id(user_id)
    order = orders_col.find_one({"_id": _ensure_object_id(order_id), "user": user_oid})
    if not order:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Order not found")
    order["status"] = "cancelled"
    orders_col.update_one({"_id": order["_id"]}, {"$set": order})
    return _serialize_order(order)

