2026-09-01 21:03:38 +02:00
<? php
declare ( strict_types = 1 );
namespace App\Domain\Reporting {
require_once __DIR__ . '/ReportFilters.php' ;
/** Deterministic, storage-agnostic filtering for a client's status history. */
final class ClientHistoryService
{
/** @param list<array<string,mixed>> $rows @return list<array<string,mixed>> */
public function filter ( array $rows , array | \ReportFilters | null $criteria = null ) : array
{
$filters = $criteria instanceof \ReportFilters ? $criteria : \ReportFilters :: fromArray ( $criteria ?? []);
$result = [];
foreach ( $rows as $row ) {
if ( ! is_array ( $row ) || ! $filters -> matches ( $row )) continue ;
$result [] = $row ;
}
usort ( $result , static fn ( array $a , array $b ) : int => strcmp (( string )( $a [ 'changed_at' ] ?? $a [ 'created_at' ] ?? '' ), ( string )( $b [ 'changed_at' ] ?? $b [ 'created_at' ] ?? '' )) ?: (( int )( $a [ 'id' ] ?? 0 ) <=> ( int )( $b [ 'id' ] ?? 0 )));
return $result ;
}
/** @return list<array<string,mixed>> */
public function forClient ( array $rows , int | string $clientId , array $criteria = []) : array
{
if ( ! is_int ( $clientId ) && ( ! is_string ( $clientId ) || preg_match ( '/^[1-9]\d*$/' , trim ( $clientId )) !== 1 )) throw new \InvalidArgumentException ( 'Client ID must be a positive integer.' );
$criteria [ 'client_id' ] = ( int ) $clientId ;
return $this -> filter ( $rows , $criteria );
}
2026-09-01 21:47:35 +02:00
/** @return array<string,mixed> */
public function validateForClient ( mixed $clientId , array $rows , array $criteria = []) : array
{
$id = $this -> positiveId ( $clientId );
if ( $id === null ) return [ 'valid' => false , 'client_id' => null , 'timeline' => [], 'errors' => [ 'client_id' => 'Client ID must be a positive integer.' ]];
return [ 'valid' => true , 'client_id' => $id , 'timeline' => $this -> forClient ( $rows , $id , $criteria ), 'errors' => []];
}
/** Controller-ready client history timeline command. */
public function timeline ( array $rows , mixed $clientId , array $criteria = []) : array
{
return $this -> validateForClient ( $clientId , $rows , $criteria );
}
2026-09-01 21:03:38 +02:00
/** @return list<array<string,mixed>> */
public function history ( array $rows , array | \ReportFilters | null $criteria = null ) : array { return $this -> filter ( $rows , $criteria ); }
/** @return list<array<string,mixed>> */
public function query ( array $rows , array | \ReportFilters | null $criteria = null ) : array { return $this -> filter ( $rows , $criteria ); }
/** @return list<array<string,mixed>> */
public function getHistory ( array $rows , array | \ReportFilters | null $criteria = null ) : array { return $this -> filter ( $rows , $criteria ); }
2026-09-01 21:47:35 +02:00
private function positiveId ( mixed $value ) : ? int
{
if ( is_int ( $value ) && $value > 0 ) return $value ;
if ( is_string ( $value ) && preg_match ( '/^[1-9]\\d*$/' , trim ( $value )) === 1 ) { $id = filter_var ( trim ( $value ), FILTER_VALIDATE_INT ); return $id === false ? null : $id ; }
return null ;
}
2026-09-01 21:03:38 +02:00
}
}
namespace {
if ( ! class_exists ( 'ClientHistoryService' , false )) class_alias ( 'App\\Domain\\Reporting\\ClientHistoryService' , 'ClientHistoryService' );
}