from datetime import date
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from app.api.workspace import get_verified_current_user
from app.db.session import get_db
from app.models.invoice import Invoice, InvoiceItem, InvoicePayment
from app.models.user import User
from app.schemas.invoice import InvoiceCreate, InvoiceUpdate, InvoicePaymentCreate
from app.services.invoices import create_invoice, get_invoice, update_invoice, payment, cancel_invoice
from app.services.workspace import get_membership_for_business
router=APIRouter(tags=["Invoices"])
def access(db,u,b):
 if not get_membership_for_business(db,u,b): raise HTTPException(404,"Business workspace not found.")
def run(db,fn):
 try:return fn()
 except LookupError as e:raise HTTPException(404,str(e))
 except ValueError as e:db.rollback();raise HTTPException(422,str(e))
 except IntegrityError:db.rollback();raise HTTPException(409,"Duplicate invoice number or sale-linked invoice.")
def view(db,i): return {"invoice":i,"items":list(db.scalars(select(InvoiceItem).where(InvoiceItem.invoice_id==i.id))),"payments":list(db.scalars(select(InvoicePayment).where(InvoicePayment.invoice_id==i.id).order_by(InvoicePayment.paid_at)))}
@router.post('/api/businesses/{business_id}/invoices',status_code=201)
def create(business_id:UUID,data:InvoiceCreate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
 access(db,user,business_id);return run(db,lambda:view(db,create_invoice(db,business_id,user.id,data)))
@router.get('/api/businesses/{business_id}/invoices')
def listing(business_id:UUID,customer_id:UUID|None=None,branch_id:UUID|None=None,status:str|None=None,paid:bool|None=None,start_date:date|None=None,end_date:date|None=None,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
 access(db,user,business_id);q=select(Invoice).where(Invoice.business_id==business_id)
 for col,val in ((Invoice.customer_id,customer_id),(Invoice.branch_id,branch_id),(Invoice.status,status)):
  if val is not None:q=q.where(col==val)
 if paid is not None:q=q.where(Invoice.balance==0 if paid else Invoice.balance>0)
 if start_date:q=q.where(Invoice.issue_date>=start_date)
 if end_date:q=q.where(Invoice.issue_date<=end_date)
 return list(db.scalars(q.order_by(Invoice.created_at.desc())))
@router.get('/api/businesses/{business_id}/invoices/{invoice_id}')
def detail(business_id:UUID,invoice_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
 access(db,user,business_id);return run(db,lambda:view(db,get_invoice(db,business_id,invoice_id)))
@router.patch('/api/businesses/{business_id}/invoices/{invoice_id}')
def update(business_id:UUID,invoice_id:UUID,data:InvoiceUpdate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
 access(db,user,business_id);return run(db,lambda:view(db,update_invoice(db,business_id,invoice_id,data)))
@router.post('/api/businesses/{business_id}/invoices/{invoice_id}/send')
def send(business_id:UUID,invoice_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
 access(db,user,business_id)
 def go():
  i=get_invoice(db,business_id,invoice_id,True)
  if i.status!="DRAFT":raise ValueError("Only draft invoices can be sent.")
  i.status="PAID" if i.balance==0 else "SENT";db.commit();db.refresh(i);return view(db,i)
 return run(db,go)
@router.post('/api/businesses/{business_id}/invoices/{invoice_id}/payments',status_code=201)
def pay(business_id:UUID,invoice_id:UUID,data:InvoicePaymentCreate,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
 access(db,user,business_id);return run(db,lambda:payment(db,business_id,invoice_id,user.id,data))
@router.get('/api/businesses/{business_id}/invoices/{invoice_id}/payments')
def payments(business_id:UUID,invoice_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
 access(db,user,business_id);run(db,lambda:get_invoice(db,business_id,invoice_id));return list(db.scalars(select(InvoicePayment).where(InvoicePayment.invoice_id==invoice_id).order_by(InvoicePayment.paid_at)))
@router.post('/api/businesses/{business_id}/invoices/{invoice_id}/cancel')
def cancel(business_id:UUID,invoice_id:UUID,user:User=Depends(get_verified_current_user),db:Session=Depends(get_db)):
 access(db,user,business_id);return run(db,lambda:view(db,cancel_invoice(db,business_id,invoice_id)))
