<?php

declare(strict_types=1);
namespace App\Services;
use App\Repositories\PaymentRepository;
use InvalidArgumentException;
use RuntimeException;

final class PaymentService
{
    public function __construct(private ?PaymentRepository $repo=null){$this->repo??=new PaymentRepository();}
    public function list(array $filters):array{return $this->repo->paginate($filters,25);}
    public function invoice(int $id):array{$invoice=$this->repo->invoice($id);if(!$invoice)throw new RuntimeException('Invoice not found.',404);$invoice['payments']=$this->repo->invoicePayments($id);$invoice['paid_amount']=$this->repo->activePaidTotal($id);$invoice['balance']=max(0,(float)$invoice['total']-$invoice['paid_amount']);return $invoice;}
    public function find(int $id):array{$p=$this->repo->find($id);if(!$p)throw new RuntimeException('Payment not found.',404);return $p;}
    public function create(int $invoiceId,array $input,int $actor):int
    {
        $invoice=$this->invoice($invoiceId);if(($invoice['status']??'')==='cancelled')throw new InvalidArgumentException('Payments cannot be recorded against a cancelled invoice.');
        if($invoice['balance']<=0)throw new InvalidArgumentException('This invoice is already fully paid.');
        $amount=$this->money($input['amount']??'','Payment amount');if($amount<=0)throw new InvalidArgumentException('Payment amount must be greater than zero.');if($amount>$invoice['balance']+0.00001)throw new InvalidArgumentException('Payment cannot exceed the outstanding balance.');
        $date=trim((string)($input['payment_date']??''));if(!preg_match('/^\d{4}-\d{2}-\d{2}$/',$date))throw new InvalidArgumentException('Enter a valid payment date.');
        $method=trim((string)($input['payment_method']??''));if($method==='')throw new InvalidArgumentException('Select a payment method.');
        $id=$this->repo->create(['invoice_id'=>$invoiceId,'amount'=>number_format($amount,2,'.',''),'payment_date'=>$date,'payment_method'=>$method,'payment_reference'=>trim((string)($input['payment_reference']??''))?:null,'notes'=>trim((string)($input['notes']??''))?:null],$actor);
        $this->repo->audit($actor,'payment_created',$id,['invoice_id'=>$invoiceId,'amount'=>$amount]);return $id;
    }
    public function void(int $id,int $actor):void{$p=$this->find($id);$this->repo->void($id,$actor);$this->repo->audit($actor,'payment_voided',$id,['invoice_id'=>(int)$p['invoice_id'],'amount'=>(float)$p['amount']]);}
    private function money(mixed $v,string $label):float{$s=trim((string)$v);if(!preg_match('/^\d+(?:\.\d{1,2})?$/',$s))throw new InvalidArgumentException($label.' must be a valid amount with up to 2 decimal places.');return (float)$s;}
}
