from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from typing import List, Optional

from app.core.database import get_db
from app.core.auth import get_current_user, requires_role
from app.models.booking import Booking
from app.models.user import User
from app.models.product import Product
from app.schemas.booking import BookingOut, BookingCreate

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

# Konstante aus deinen Policies: Nutzer dürfen nicht < 0 € fallen (Stand 2025-07-02).
ALLOWED_OVERDRAFT_CENTS = 0  # falls -500 erlaubt sein soll: setze auf -500


@router.post("/", response_model=BookingOut, status_code=status.HTTP_201_CREATED)
def create_booking(
    booking: BookingCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if current_user.role not in ("manager", "admin") and booking.user_id != current_user.id:
        raise HTTPException(status_code=403, detail="Forbidden")

    if booking.amount is None or booking.amount <= 0:
        raise HTTPException(status_code=400, detail="Invalid amount")

    # Produkt mit Row-Lock
    product = (
        db.query(Product)
        .filter(Product.id == booking.product_id)
        .with_for_update()
        .first()
    )
    if not product:
        raise HTTPException(status_code=404, detail="Product not found")
    if hasattr(product, "is_active") and not bool(product.is_active):
        raise HTTPException(status_code=400, detail="Product is not active")

    qty = int(booking.amount)
    # Bestand prüfen/abziehen, falls Feld vorhanden
    if hasattr(product, "stock"):
        current_stock = int(product.stock or 0)
        if current_stock < qty:
            raise HTTPException(status_code=409, detail="Not enough stock")
        product.stock = current_stock - qty

    server_total = int(product.price_cents) * qty

    try:
        # User mit Row-Lock
        user = (
            db.query(User)
            .filter(User.id == booking.user_id)
            .with_for_update()
            .first()
        )
        if not user:
            raise HTTPException(status_code=404, detail="User not found")

        new_balance = int(user.balance_cents) - server_total
        if new_balance < -ALLOWED_OVERDRAFT_CENTS:
            raise HTTPException(status_code=400, detail="Insufficient balance")

        user.balance_cents = new_balance

        db_booking = Booking(
            user_id=booking.user_id,
            product_id=booking.product_id,
            amount=qty,
            total_cents=server_total,   # Client-Wert wird ignoriert
        )
        db.add(db_booking)
        db.flush()
        db.refresh(db_booking)
        db.commit()
        return db_booking
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def _role_name(r) -> str:
    # robust gegen Enum/String/None
    if r is None:
        return ""
    v = getattr(r, "value", r)
    return str(v).strip().lower()

@router.get("/", response_model=List[BookingOut])
def list_bookings(
    limit: int = Query(100, ge=1, le=1000),
    offset: int = Query(0, ge=0),
    user_id: Optional[int] = Query(None, ge=1),
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    q = db.query(Booking)

    role = _role_name(getattr(current_user, "role", None))
    is_manager = role in ("manager", "admin")

    if is_manager:
        # Admin/Manager: optionaler Filter auf user_id
        if user_id is not None:
            q = q.filter(Booking.user_id == user_id)
    else:
        # Normale Nutzer: strikt nur eigene Buchungen
        q = q.filter(Booking.user_id == current_user.id)

    # stabile, absteigende Reihenfolge – Zeitfeld optional
    order_cols = []
    if hasattr(Booking, "created_at"):
        order_cols.append(Booking.created_at.desc())
    elif hasattr(Booking, "timestamp"):
        order_cols.append(Booking.timestamp.desc())
    elif hasattr(Booking, "created"):
        order_cols.append(Booking.created.desc())
    order_cols.append(Booking.id.desc())

    return q.order_by(*order_cols).limit(limit).offset(offset).all()

@router.get("/me", response_model=List[BookingOut])
def list_my_bookings(
    limit: int = Query(100, ge=1, le=1000),
    offset: int = Query(0, ge=0),
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    q = (
        db.query(Booking)
        .filter(Booking.user_id == current_user.id)
        .order_by(Booking.id.desc())
        .limit(limit)
        .offset(offset)
    )
    return q.all()


@router.get("/{booking_id}", response_model=BookingOut)
def get_booking(
    booking_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    b = db.query(Booking).filter(Booking.id == booking_id).first()
    if not b:
        raise HTTPException(status_code=404, detail="Booking not found")
    if b.user_id != current_user.id and current_user.role not in ("manager", "admin"):
        raise HTTPException(status_code=403, detail="Forbidden")
    return b


@router.delete("/{booking_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_booking(
    booking_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    requires_role("manager", "admin")(current_user)
    b = db.query(Booking).filter(Booking.id == booking_id).first()
    if not b:
        raise HTTPException(status_code=404, detail="Booking not found")
    db.delete(b)
    db.commit()
    return None
