from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import Optional, List
from . import repository
from .schema import (
    CartCreate, CartUpdate, CartResponse,
    CartitemCreate, CartitemUpdate, CartitemResponse,
    WishlistCreate, WishlistUpdate, WishlistResponse,
    WishlistitemCreate, WishlistitemUpdate, WishlistitemResponse,
    CartDetailResponse, WishlistDetailResponse
)
from .models import Cart, Cartitem, Wishlist, Wishlistitem
from user_management import repository as user_repo
from listing_management import repository as listing_repo
from card_catalog import repository as card_repo


def _get_or_raise(db: Session, entity_id: str, model_class, entity_name: str):
    obj = repository.get_by_id(db, entity_id, model_class)
    if not obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"{entity_name} not found")
    return obj


def create_cart(db: Session, data: CartCreate) -> CartResponse:
    user = user_repo.get_by_id(db, data.user_id)
    if not user:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User not found")
    
    existing_cart = repository.get_cart_by_user_id(db, data.user_id)
    if existing_cart:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="User already has a cart")
    
    try:
        cart = repository.create(db, Cart, data.model_dump())
        db.commit()
        db.refresh(cart)
        return CartResponse.model_validate(cart)
    except Exception:
        db.rollback()
        raise


def get_cart(db: Session, cart_id: str) -> CartResponse:
    cart = _get_or_raise(db, cart_id, Cart, "Cart")
    return CartResponse.model_validate(cart)


def list_carts(db: Session, limit: int, offset: int, user_id: Optional[str] = None) -> List[CartResponse]:
    filters = {}
    if user_id:
        filters["user_id"] = user_id
    carts = repository.list_all(db, Cart, limit, offset, **filters)
    return [CartResponse.model_validate(cart) for cart in carts]


def update_cart(db: Session, cart_id: str, data: CartUpdate) -> CartResponse:
    update_data = data.model_dump(exclude_unset=True)
    if not update_data:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="No fields to update")
    
    if "user_id" in update_data:
        user = user_repo.get_by_id(db, update_data["user_id"])
        if not user:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User not found")
    
    try:
        cart = repository.update(db, cart_id, Cart, update_data)
        if not cart:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cart not found")
        db.commit()
        db.refresh(cart)
        return CartResponse.model_validate(cart)
    except Exception:
        db.rollback()
        raise


def delete_cart(db: Session, cart_id: str) -> dict:
    try:
        success = repository.delete(db, cart_id, Cart)
        if not success:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cart not found")
        db.commit()
        return {"message": "Cart deleted successfully"}
    except Exception:
        db.rollback()
        raise


def create_cartitem(db: Session, data: CartitemCreate) -> CartitemResponse:
    cart = repository.get_by_id(db, data.cart_id, Cart)
    if not cart:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Cart not found")
    
    listing = listing_repo.get_by_id(db, data.listing_id)
    if not listing:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Listing not found")
    
    if listing.quantity < data.quantity:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Insufficient listing quantity")
    
    try:
        cartitem = repository.create(db, Cartitem, data.model_dump())
        db.commit()
        db.refresh(cartitem)
        return CartitemResponse.model_validate(cartitem)
    except Exception:
        db.rollback()
        raise


def get_cartitem(db: Session, cartitem_id: str) -> CartitemResponse:
    cartitem = _get_or_raise(db, cartitem_id, Cartitem, "Cart item")
    return CartitemResponse.model_validate(cartitem)


def list_cartitems(db: Session, limit: int, offset: int, cart_id: Optional[str] = None, listing_id: Optional[str] = None) -> List[CartitemResponse]:
    filters = {}
    if cart_id:
        filters["cart_id"] = cart_id
    if listing_id:
        filters["listing_id"] = listing_id
    cartitems = repository.list_all(db, Cartitem, limit, offset, **filters)
    return [CartitemResponse.model_validate(item) for item in cartitems]


def update_cartitem(db: Session, cartitem_id: str, data: CartitemUpdate) -> CartitemResponse:
    update_data = data.model_dump(exclude_unset=True)
    if not update_data:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="No fields to update")
    
    if "cart_id" in update_data:
        cart = repository.get_by_id(db, update_data["cart_id"], Cart)
        if not cart:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Cart not found")
    
    if "listing_id" in update_data:
        listing = listing_repo.get_by_id(db, update_data["listing_id"])
        if not listing:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Listing not found")
    
    if "quantity" in update_data:
        cartitem = repository.get_by_id(db, cartitem_id, Cartitem)
        if cartitem:
            listing = listing_repo.get_by_id(db, cartitem.listing_id)
            if listing and listing.quantity < update_data["quantity"]:
                raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Insufficient listing quantity")
    
    try:
        cartitem = repository.update(db, cartitem_id, Cartitem, update_data)
        if not cartitem:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cart item not found")
        db.commit()
        db.refresh(cartitem)
        return CartitemResponse.model_validate(cartitem)
    except Exception:
        db.rollback()
        raise


def delete_cartitem(db: Session, cartitem_id: str) -> dict:
    try:
        success = repository.delete(db, cartitem_id, Cartitem)
        if not success:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cart item not found")
        db.commit()
        return {"message": "Cart item deleted successfully"}
    except Exception:
        db.rollback()
        raise


def create_wishlist(db: Session, data: WishlistCreate) -> WishlistResponse:
    user = user_repo.get_by_id(db, data.user_id)
    if not user:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User not found")
    
    try:
        wishlist = repository.create(db, Wishlist, data.model_dump())
        db.commit()
        db.refresh(wishlist)
        return WishlistResponse.model_validate(wishlist)
    except Exception:
        db.rollback()
        raise


def get_wishlist(db: Session, wishlist_id: str) -> WishlistResponse:
    wishlist = _get_or_raise(db, wishlist_id, Wishlist, "Wishlist")
    return WishlistResponse.model_validate(wishlist)


def list_wishlists(db: Session, limit: int, offset: int, user_id: Optional[str] = None, is_public: Optional[bool] = None) -> List[WishlistResponse]:
    filters = {}
    if user_id:
        filters["user_id"] = user_id
    if is_public is not None:
        filters["is_public"] = is_public
    wishlists = repository.list_all(db, Wishlist, limit, offset, **filters)
    return [WishlistResponse.model_validate(wishlist) for wishlist in wishlists]


def update_wishlist(db: Session, wishlist_id: str, data: WishlistUpdate) -> WishlistResponse:
    update_data = data.model_dump(exclude_unset=True)
    if not update_data:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="No fields to update")
    
    if "user_id" in update_data:
        user = user_repo.get_by_id(db, update_data["user_id"])
        if not user:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User not found")
    
    try:
        wishlist = repository.update(db, wishlist_id, Wishlist, update_data)
        if not wishlist:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Wishlist not found")
        db.commit()
        db.refresh(wishlist)
        return WishlistResponse.model_validate(wishlist)
    except Exception:
        db.rollback()
        raise


def delete_wishlist(db: Session, wishlist_id: str) -> dict:
    try:
        success = repository.delete(db, wishlist_id, Wishlist)
        if not success:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Wishlist not found")
        db.commit()
        return {"message": "Wishlist deleted successfully"}
    except Exception:
        db.rollback()
        raise


def create_wishlistitem(db: Session, data: WishlistitemCreate) -> WishlistitemResponse:
    wishlist = repository.get_by_id(db, data.wishlist_id, Wishlist)
    if not wishlist:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Wishlist not found")
    
    card = card_repo.get_by_id(db, data.card_id)
    if not card:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Card not found")
    
    try:
        wishlistitem = repository.create(db, Wishlistitem, data.model_dump())
        db.commit()
        db.refresh(wishlistitem)
        return WishlistitemResponse.model_validate(wishlistitem)
    except Exception:
        db.rollback()
        raise


def get_wishlistitem(db: Session, wishlistitem_id: str) -> WishlistitemResponse:
    wishlistitem = _get_or_raise(db, wishlistitem_id, Wishlistitem, "Wishlist item")
    return WishlistitemResponse.model_validate(wishlistitem)


def list_wishlistitems(db: Session, limit: int, offset: int, wishlist_id: Optional[str] = None, card_id: Optional[str] = None) -> List[WishlistitemResponse]:
    filters = {}
    if wishlist_id:
        filters["wishlist_id"] = wishlist_id
    if card_id:
        filters["card_id"] = card_id
    wishlistitems = repository.list_all(db, Wishlistitem, limit, offset, **filters)
    return [WishlistitemResponse.model_validate(item) for item in wishlistitems]


def update_wishlistitem(db: Session, wishlistitem_id: str, data: WishlistitemUpdate) -> WishlistitemResponse:
    update_data = data.model_dump(exclude_unset=True)
    if not update_data:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="No fields to update")
    
    if "wishlist_id" in update_data:
        wishlist = repository.get_by_id(db, update_data["wishlist_id"], Wishlist)
        if not wishlist:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Wishlist not found")
    
    if "card_id" in update_data:
        card = card_repo.get_by_id(db, update_data["card_id"])
        if not card:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Card not found")
    
    try:
        wishlistitem = repository.update(db, wishlistitem_id, Wishlistitem, update_data)
        if not wishlistitem:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Wishlist item not found")
        db.commit()
        db.refresh(wishlistitem)
        return WishlistitemResponse.model_validate(wishlistitem)
    except Exception:
        db.rollback()
        raise


def delete_wishlistitem(db: Session, wishlistitem_id: str) -> dict:
    try:
        success = repository.delete(db, wishlistitem_id, Wishlistitem)
        if not success:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Wishlist item not found")
        db.commit()
        return {"message": "Wishlist item deleted successfully"}
    except Exception:
        db.rollback()
        raise


def get_cart_details(db: Session, cart_id: str) -> CartDetailResponse:
    cart = repository.get_cart_with_details(db, cart_id)
    if not cart:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cart not found")
    return CartDetailResponse.model_validate(cart)


def get_wishlist_details(db: Session, wishlist_id: str) -> WishlistDetailResponse:
    wishlist = repository.get_wishlist_with_details(db, wishlist_id)
    if not wishlist:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Wishlist not found")
    return WishlistDetailResponse.model_validate(wishlist)