from sqlalchemy import select
from app.models.expense_income import Expense, ExpenseCategory, OtherIncome, OtherIncomeCategory
from app.services.products import require_branch
from app.services.audit import record_audit

def category(db, cls, business_id, category_id):
    obj=db.scalar(select(cls).where(cls.id==category_id, cls.business_id==business_id))
    if not obj: raise LookupError("Category not found.")
    return obj
def entry(db, cls, business_id, entry_id, lock=False):
    q=select(cls).where(cls.id==entry_id, cls.business_id==business_id)
    if lock: q=q.with_for_update()
    obj=db.scalar(q)
    if not obj: raise LookupError("Record not found.")
    return obj
def create_category(db, cls, business_id, data):
    obj=cls(business_id=business_id, **data.model_dump()); db.add(obj); db.commit(); db.refresh(obj); return obj
def update_category(db, cls, business_id, category_id, data):
    obj=category(db,cls,business_id,category_id)
    for k,v in data.model_dump(exclude_unset=True).items(): setattr(obj,k,v)
    db.commit(); db.refresh(obj); return obj
def create_entry(db, cls, category_cls, business_id, user_id, data):
    values=data.model_dump(); cat=category(db,category_cls,business_id,values["category_id"])
    if "payment_method" in values and values["payment_method"] not in {"CASH", "POS", "TRANSFER", "OTHER"}: raise ValueError("Invalid payment method.")
    if not cat.is_active: raise ValueError("Category is inactive.")
    if values.get("branch_id"): require_branch(db,business_id,values["branch_id"])
    obj=cls(business_id=business_id,recorded_by=user_id,status="ACTIVE",**values); db.add(obj); db.flush(); record_audit(db,business_id,'EXPENSE_CREATED' if cls is Expense else 'INCOME_CREATED','expense' if cls is Expense else 'other_income',obj.id,user_id,obj.branch_id,{'amount':str(obj.amount),'category_id':str(obj.category_id)}); db.commit(); db.refresh(obj); return obj
def update_entry(db, cls, category_cls, business_id, entry_id, data):
    obj=entry(db,cls,business_id,entry_id,True)
    if obj.status != "ACTIVE": raise ValueError("Cancelled records cannot be edited.")
    values=data.model_dump(exclude_unset=True)
    if "payment_method" in values and values["payment_method"] not in {"CASH", "POS", "TRANSFER", "OTHER"}: raise ValueError("Invalid payment method.")
    if "category_id" in values:
        cat=category(db,category_cls,business_id,values["category_id"])
        if not cat.is_active: raise ValueError("Category is inactive.")
    if values.get("branch_id"): require_branch(db,business_id,values["branch_id"])
    for k,v in values.items(): setattr(obj,k,v)
    db.commit(); db.refresh(obj); return obj
def cancel_entry(db, cls, business_id, entry_id):
    obj=entry(db,cls,business_id,entry_id,True)
    if obj.status != "ACTIVE": raise ValueError("Record is already cancelled.")
    obj.status="CANCELLED"; record_audit(db,business_id,'EXPENSE_CANCELLED' if cls is Expense else 'INCOME_CANCELLED','expense' if cls is Expense else 'other_income',obj.id,obj.recorded_by,obj.branch_id,{}); db.commit(); db.refresh(obj); return obj
