from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.orm import Session

from app.api.workspace import get_verified_current_user
from app.core.permissions import Permission
from app.db.session import get_db
from app.models.loyalty import LoyaltyTransaction
from app.models.user import User
from app.schemas.loyalty import LoyaltyAdjustmentRequest, LoyaltyRedeemRequest, LoyaltySettingsUpdate
from app.services.loyalty import add_debit_or_adjustment, customer_or_error, earn_for_sale, get_settings, summary
from app.services.rbac import require_permission

router = APIRouter(tags=["Loyalty"])

def permitted(db, user, business_id, permission):
    return require_permission(db, user, business_id, permission)

def fail(fn):
    try: return fn()
    except LookupError as exc: raise HTTPException(404, str(exc))
    except ValueError as exc: raise HTTPException(422, str(exc))

@router.get("/api/businesses/{business_id}/loyalty/settings")
def settings_get(business_id: UUID, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    permitted(db, user, business_id, Permission.VIEW_LOYALTY)
    value = get_settings(db, business_id, create=True); db.commit(); db.refresh(value); return value

@router.patch("/api/businesses/{business_id}/loyalty/settings")
def settings_patch(business_id: UUID, data: LoyaltySettingsUpdate, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    permitted(db, user, business_id, Permission.MANAGE_LOYALTY)
    value = get_settings(db, business_id, create=True)
    for key, item in data.model_dump(exclude_unset=True).items(): setattr(value, key, item)
    db.commit(); db.refresh(value); return value

@router.get("/api/businesses/{business_id}/customers/{customer_id}/loyalty")
def customer_summary(business_id: UUID, customer_id: UUID, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    permitted(db, user, business_id, Permission.VIEW_LOYALTY)
    return fail(lambda: summary(db, business_id, customer_id))

@router.get("/api/businesses/{business_id}/customers/{customer_id}/loyalty/history")
def history(business_id: UUID, customer_id: UUID, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    permitted(db, user, business_id, Permission.VIEW_LOYALTY)
    fail(lambda: customer_or_error(db, business_id, customer_id))
    return list(db.scalars(select(LoyaltyTransaction).where(LoyaltyTransaction.business_id == business_id, LoyaltyTransaction.customer_id == customer_id).order_by(LoyaltyTransaction.created_at.desc())))

@router.post("/api/businesses/{business_id}/sales/{sale_id}/loyalty/earn")
def earn(business_id: UUID, sale_id: UUID, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    permitted(db, user, business_id, Permission.OPERATE_LOYALTY)
    transaction, created = fail(lambda: earn_for_sale(db, business_id, sale_id, user.id))
    return {"transaction": transaction, "created": created}

@router.post("/api/businesses/{business_id}/customers/{customer_id}/loyalty/redeem")
def redeem(business_id: UUID, customer_id: UUID, data: LoyaltyRedeemRequest, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    permitted(db, user, business_id, Permission.OPERATE_LOYALTY)
    settings = get_settings(db, business_id, create=True)
    if not settings.enabled: raise HTTPException(422, "Loyalty is disabled for this business.")
    return fail(lambda: add_debit_or_adjustment(db, business_id, customer_id, user.id, "REDEEM", -data.points, data.reference, data.notes))

@router.post("/api/businesses/{business_id}/customers/{customer_id}/loyalty/adjust")
def adjust(business_id: UUID, customer_id: UUID, data: LoyaltyAdjustmentRequest, user: User = Depends(get_verified_current_user), db: Session = Depends(get_db)):
    permitted(db, user, business_id, Permission.MANAGE_LOYALTY)
    if data.points == 0: raise HTTPException(422, "Adjustment points cannot be zero.")
    return fail(lambda: add_debit_or_adjustment(db, business_id, customer_id, user.id, "ADJUSTMENT", data.points, data.reference, data.notes))
