from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session, select, delete
from geopy.geocoders import Nominatim
from geopy.distance import geodesic
from backend.database import get_session
from backend.routes.auth import get_current_user
from backend.models.models import Order, OrderItem, User, Product, Shop
from backend.schemas.order import OrderCreate, OrderRead, OrderUpdate

router = APIRouter()


@router.post("/", response_model=OrderRead)
def create_order(
    order_data: OrderCreate,
    session: Session = Depends(get_session),
    current_user: User = Depends(get_current_user),
):
    # Fetch the shop details
    shop = session.get(Shop, order_data.shop_id)
    if not shop:
        raise HTTPException(status_code=404, detail="Shop not found")

    # Geocode the delivery address
    geolocator = Nominatim(user_agent="order_locator")
    delivery_location = geolocator.geocode(order_data.delivery_address)
    if not delivery_location:
        raise HTTPException(status_code=400, detail="Invalid delivery address provided")

    # Calculate the distance between the shop and the delivery location
    shop_location = (shop.latitude, shop.longitude)
    delivery_coordinates = (delivery_location.latitude, delivery_location.longitude)
    distance_km = geodesic(shop_location, delivery_coordinates).kilometers

    # Calculate the shipping price ($1 per km)
    shipping_price = distance_km * 1.0

    total_price = shipping_price
    for item in order_data.items:
        product = session.get(Product, item.product_id)
        if not product or product.stock < item.quantity:
            raise HTTPException(
                status_code=400, detail=f"Product {item.product_id} is out of stock"
            )
        total_price += item.quantity * product.price

    # Create the order
    new_order = Order(
        user_id=current_user.id,
        shop_id=order_data.shop_id,
        total_price=total_price,
        shipping_price=shipping_price,
        status="pending",
        delivery_address=order_data.delivery_address,
        delivery_latitude=delivery_location.latitude,
        delivery_longitude=delivery_location.longitude,
    )
    session.add(new_order)
    session.commit()
    session.refresh(new_order)

    # Create order items
    for item in order_data.items:
        order_item = OrderItem(
            order_id=new_order.id,
            product_id=item.product_id,
            quantity=item.quantity,
            price=product.price,
        )
        session.add(order_item)
        product.stock -= item.quantity

    session.commit()
    return new_order


# list all orders associated with the current user
@router.get("/list", response_model=list[OrderRead])
def list_orders(
    session: Session = Depends(get_session),
    current_user: User = Depends(get_current_user),
):
    orders = session.exec(select(Order).where(Order.user_id == current_user.id)).all()
    return orders


# retrieve a specific order, only the owner can access
@router.get("/{order_id}", response_model=OrderRead)
def get_order(
    order_id: int,
    session: Session = Depends(get_session),
    current_user: User = Depends(get_current_user),
):
    order = session.get(Order, order_id)
    if not order or order.user_id != current_user.id:
        raise HTTPException(status_code=404, detail="Order not found")
    return order


# update order status
@router.put("/status/{order_id}", response_model=OrderRead)
def update_order(
    order_id: int,
    order_update: OrderUpdate,
    session: Session = Depends(get_session),
    current_user: User = Depends(get_current_user),
):
    order = session.get(Order, order_id)
    if not order or order.user_id != current_user.id:
        raise HTTPException(status_code=404, detail="Order not found or unauthorized")

    # Update the delivery address if provided
    if order_update.delivery_address:
        geolocator = Nominatim(user_agent="order_locator")
        delivery_location = geolocator.geocode(order_update.delivery_address)
        if not delivery_location:
            raise HTTPException(status_code=400, detail="Invalid delivery address")
        order.delivery_address = order_update.delivery_address
        order.delivery_latitude = delivery_location.latitude
        order.delivery_longitude = delivery_location.longitude

        # Recalculate the shipping price
        shop = session.get(Shop, order.shop_id)
        if not shop:
            raise HTTPException(status_code=404, detail="Shop not found")
        shop_location = (shop.latitude, shop.longitude)
        delivery_coordinates = (delivery_location.latitude, delivery_location.longitude)
        distance_km = geodesic(shop_location, delivery_coordinates).kilometers
        shipping_price = distance_km * 1.0

        # Update the total price
        product_total = sum(
            item.quantity * session.get(Product, item.product_id).price
            for item in order.order_items
        )
        order.shipping_price = shipping_price
        order.total_price = product_total + shipping_price

    # Update the order status if provided
    if order_update.status:
        if order.status != "pending" or order_update.status != "completed":
            raise HTTPException(
                status_code=400,
                detail="Order can only be updated from pending to completed",
            )
        order.status = order_update.status

    session.commit()
    session.refresh(order)
    return order


# Delete order
@router.delete("/{order_id}")
def delete_order(
    order_id: int,
    session: Session = Depends(get_session),
    current_user: User = Depends(get_current_user),
):
    order = session.get(Order, order_id)
    if not order or (
        order.user_id != current_user.id and current_user.role != "shop_owner"
    ):
        raise HTTPException(status_code=404, detail="Order not found or unauthorized")

    # Delete related order items first
    session.exec(delete(OrderItem).where(OrderItem.order_id == order.id))

    # Now delete the order
    session.delete(order)
    session.commit()

    return {"message": "Order deleted successfully"}