from uuid import UUID
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.models.customer import Customer
from app.models.delivery import Delivery, DeliveryStatusHistory
from app.models.sale import Sale
from app.services.audit import record_audit

class DeliveryError(ValueError): pass

def delivery(db: Session, business_id: UUID, delivery_id: UUID, lock=False):
    q = select(Delivery).where(Delivery.id == delivery_id, Delivery.business_id == business_id)
    if lock: q = q.with_for_update()
    obj = db.scalar(q)
    if obj is None: raise LookupError("Delivery not found.")
    return obj

def validate_customer_sale(db, business_id, customer_id, sale_id):
    if db.scalar(select(Customer.id).where(Customer.id == customer_id, Customer.business_id == business_id)) is None:
        raise DeliveryError("Customer does not belong to this business.")
    if sale_id is not None:
        sale = db.scalar(select(Sale).where(Sale.id == sale_id, Sale.business_id == business_id))
        if sale is None: raise DeliveryError("Sale does not belong to this business.")
        if sale.customer_id != customer_id: raise DeliveryError("Sale customer must match delivery customer.")

def create_delivery(db, business_id, user_id, data):
    validate_customer_sale(db, business_id, data.customer_id, data.sale_id)
    obj = Delivery(business_id=business_id, created_by=user_id, **data.model_dump())
    db.add(obj); db.flush()
    db.add(DeliveryStatusHistory(delivery_id=obj.id, previous_status=None, new_status="PENDING", changed_by=user_id, note="Delivery created."))
    record_audit(db,business_id,'DELIVERY_CREATED','delivery',obj.id,user_id,obj.branch_id,{'tracking_reference':obj.tracking_reference}); db.commit(); db.refresh(obj); return obj

def transition(db, business_id, delivery_id, user_id, data):
    obj = delivery(db, business_id, delivery_id, True)
    allowed = {"PENDING": {"IN_TRANSIT", "CANCELLED"}, "IN_TRANSIT": {"DELIVERED", "CANCELLED"}}
    if data.status not in allowed.get(obj.status, set()): raise DeliveryError(f"Cannot transition delivery from {obj.status} to {data.status}.")
    old = obj.status; obj.status = data.status
    db.add(DeliveryStatusHistory(delivery_id=obj.id, previous_status=old, new_status=data.status, changed_by=user_id, note=data.note))
    record_audit(db,business_id,'DELIVERY_STATUS_CHANGED','delivery',obj.id,user_id,obj.branch_id,{'previous_status':old,'new_status':data.status,'tracking_reference':obj.tracking_reference}); db.commit(); db.refresh(obj); return obj
