from datetime import date, datetime, time, timezone
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Query
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.delivery import Delivery, DeliveryStatusHistory
from app.models.membership import MembershipBranch
from app.models.user import User
from app.schemas.delivery import DeliveryCreate, DeliveryHistoryRead, DeliveryRead, DeliveryStatusUpdate, DeliveryUpdate
from app.services.deliveries import DeliveryError, create_delivery, delivery, transition
from app.services.audit import record_audit
from app.services.rbac import require_branch_access, require_permission

router=APIRouter(tags=["Deliveries"])
def fail(e):
    if isinstance(e, LookupError): raise HTTPException(404, str(e))
    if isinstance(e, DeliveryError): raise HTTPException(422, str(e))
    raise e
def membership(db,u,b): return require_permission(db,u,b,Permission.OPERATE_SALES)
@router.post('/api/businesses/{business_id}/deliveries', response_model=DeliveryRead, status_code=201)
def create(business_id:UUID,data:DeliveryCreate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    m=membership(db,user,business_id); require_branch_access(db,m,business_id,data.branch_id)
    try:return create_delivery(db,business_id,user.id,data)
    except Exception as e: db.rollback();fail(e)
@router.get('/api/businesses/{business_id}/deliveries',response_model=list[DeliveryRead])
def listing(business_id:UUID,branch_id:UUID|None=Query(default=None,alias='branch'),customer_id:UUID|None=Query(default=None,alias='customer'),status_filter:str|None=Query(default=None,alias='status'),sale_id:UUID|None=Query(default=None,alias='sale'),start_date:date|None=None,end_date:date|None=None,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    m=membership(db,user,business_id)
    if branch_id: require_branch_access(db,m,business_id,branch_id)
    q=select(Delivery).where(Delivery.business_id==business_id)
    if not m.all_branches: q=q.where(Delivery.branch_id.in_(select(MembershipBranch.branch_id).where(MembershipBranch.membership_id==m.id)))
    for col,val in ((Delivery.branch_id,branch_id),(Delivery.customer_id,customer_id),(Delivery.sale_id,sale_id)):
        if val:q=q.where(col==val)
    if status_filter:q=q.where(Delivery.status==status_filter.upper())
    if start_date:q=q.where(Delivery.created_at>=datetime.combine(start_date,time.min,tzinfo=timezone.utc))
    if end_date:q=q.where(Delivery.created_at<=datetime.combine(end_date,time.max,tzinfo=timezone.utc))
    return list(db.scalars(q.order_by(Delivery.created_at.desc())))
@router.get('/api/businesses/{business_id}/deliveries/{delivery_id}',response_model=DeliveryRead)
def detail(business_id:UUID,delivery_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    m=membership(db,user,business_id)
    try: obj=delivery(db,business_id,delivery_id);require_branch_access(db,m,business_id,obj.branch_id);return obj
    except Exception as e:fail(e)
@router.patch('/api/businesses/{business_id}/deliveries/{delivery_id}',response_model=DeliveryRead)
def update(business_id:UUID,delivery_id:UUID,data:DeliveryUpdate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    m=membership(db,user,business_id)
    try:
        obj=delivery(db,business_id,delivery_id,True);require_branch_access(db,m,business_id,obj.branch_id)
        if obj.status!='PENDING':
            raise DeliveryError('Only pending deliveries can be edited.')

        changes = data.model_dump(exclude_unset=True)

        previous = {
            key: getattr(obj, key)
            for key in changes
        }

        for key, value in changes.items():
            setattr(obj, key, value)

        if changes:
            record_audit(
                db,
                business_id,
                'DELIVERY_UPDATED',
                'delivery',
                obj.id,
                user.id,
                obj.branch_id,
                {
                    'tracking_reference': obj.tracking_reference,
                    'changed_fields': list(changes.keys()),
                    'previous': {
                        key: str(value) if value is not None else None
                        for key, value in previous.items()
                    },
                },
            )

        db.commit()
        db.refresh(obj)
        return obj
    except Exception as e:db.rollback();fail(e)
@router.post('/api/businesses/{business_id}/deliveries/{delivery_id}/status',response_model=DeliveryRead)
def change_status(business_id:UUID,delivery_id:UUID,data:DeliveryStatusUpdate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    m=membership(db,user,business_id)
    try:
        obj=delivery(db,business_id,delivery_id);require_branch_access(db,m,business_id,obj.branch_id);return transition(db,business_id,delivery_id,user.id,data)
    except Exception as e:db.rollback();fail(e)
@router.get('/api/businesses/{business_id}/deliveries/{delivery_id}/history',response_model=list[DeliveryHistoryRead])
def history(business_id:UUID,delivery_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
    m=membership(db,user,business_id)
    try:
        obj=delivery(db,business_id,delivery_id);require_branch_access(db,m,business_id,obj.branch_id)
        return list(db.scalars(select(DeliveryStatusHistory).where(DeliveryStatusHistory.delivery_id==delivery_id).order_by(DeliveryStatusHistory.changed_at)))
    except Exception as e:fail(e)
