<?php

namespace App\Services\Accounting;

use App\Enums\DocumentStatus;
use App\Models\DriverAdvance;
use App\Models\DriverSettlement;
use App\Services\Audit\AuditLogger;
use App\Support\Decimal;
use DomainException;
use Illuminate\Support\Facades\DB;

final class DriverSettlementPostingService
{
    public function __construct(private readonly AuthorizationGuard $authorization,private readonly DocumentApprovalGuard $guard,private readonly JournalPostingService $journals,private readonly AuditLogger $audit){}
    public function post(DriverSettlement $settlement):DriverSettlement{return DB::transaction(function()use($settlement){$settlement=DriverSettlement::query()->lockForUpdate()->findOrFail($settlement->id);$advance=DriverAdvance::query()->lockForUpdate()->findOrFail($settlement->driver_advance_id);$this->authorization->ensureCan('driver_advances.post',(int)$settlement->company_id,(int)$settlement->branch_id);$this->guard->ensureApproved($settlement);if($advance->status!==DocumentStatus::POSTED)throw new DomainException('The driver advance must be posted before settlement.');$expected=Decimal::add($settlement->expense_amount,$settlement->cash_refund_amount);if(!Decimal::equals($expected,$settlement->total_settled)||Decimal::compare($expected,'0')<=0)throw new DomainException('Settlement totals are invalid.');$open=Decimal::subtract($advance->amount,$advance->settled_amount);if(Decimal::compare($expected,$open)>0)throw new DomainException('Settlement exceeds the open driver advance.');$lines=[];if(Decimal::compare($settlement->expense_amount,'0')>0){if(!$settlement->expense_account_id)throw new DomainException('Expense account is required.');$lines[]=['account_id'=>$settlement->expense_account_id,'debit'=>$settlement->expense_amount,'description'=>'Approved driver expenses'];}if(Decimal::compare($settlement->cash_refund_amount,'0')>0){if(!$settlement->cash_bank_account_id)throw new DomainException('Cash/bank account is required for refund.');$lines[]=['account_id'=>$settlement->cash_bank_account_id,'debit'=>$settlement->cash_refund_amount,'description'=>'Refund received from driver'];}$lines[]=['account_id'=>$advance->advance_account_id,'credit'=>$expected,'description'=>'Driver advance settled'];$this->journals->post($settlement,['company_id'=>$settlement->company_id,'branch_id'=>$settlement->branch_id,'posting_date'=>$settlement->settlement_date,'document_date'=>$settlement->settlement_date,'reference_number'=>$settlement->settlement_number,'source_module'=>'driver_settlements','description'=>'Driver settlement '.$settlement->settlement_number],$lines);$settlement->forceFill(['status'=>DocumentStatus::POSTED,'posted_at'=>now()])->save();$advance->forceFill(['settled_amount'=>Decimal::add($advance->settled_amount,$expected)])->save();$this->audit->log('posted','driver_settlements',$settlement,[],['status'=>'posted','advance'=>$advance->advance_number]);return$settlement->refresh();},3);}
}
