File: /home/xedaptot/ai.naniguide.com/app/Services/Customer/VerificationServerService.php
<?php
namespace App\Services\Customer;
use App\Dto\Refactor\VerificationServer\VerificationServerSidebarStatsDto;
use App\Model\Customer;
use App\Model\EmailVerificationServer;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/**
* Read aggregator for the customer-side rich-redesigned "Email Verification
* Servers" page. Owns: stats meta, filtered list, sidebar widgets.
*/
class VerificationServerService
{
/**
* Header inline-meta (used by mc-banner-meta).
*
* @return array{total:int,active:int,inactive:int,vendors:int}
*/
public function headerMeta(Customer $customer): array
{
$base = EmailVerificationServer::query()->where('customer_id', $customer->id);
$total = (clone $base)->count();
$active = (clone $base)->where('status', EmailVerificationServer::STATUS_ACTIVE)->count();
$inactive = max(0, $total - $active);
$vendors = (clone $base)->distinct('type')->count('type');
return [
'total' => $total,
'active' => $active,
'inactive' => $inactive,
'vendors' => $vendors,
];
}
public function list(
Customer $customer,
?string $keyword = null,
?string $status = null,
string $sortBy = 'created_at',
string $sortDir = 'desc',
int $perPage = 25,
): LengthAwarePaginator {
$query = EmailVerificationServer::query()->where('customer_id', $customer->id);
if ($keyword !== null && $keyword !== '') {
$query->where('name', 'like', '%' . $keyword . '%');
}
if ($status === 'active') {
$query->where('status', EmailVerificationServer::STATUS_ACTIVE);
} elseif ($status === 'inactive') {
$query->where(function ($q) {
$q->where('status', '!=', EmailVerificationServer::STATUS_ACTIVE)
->orWhereNull('status');
});
}
$allowedSorts = ['created_at', 'name', 'type', 'status'];
if (! in_array($sortBy, $allowedSorts, true)) {
$sortBy = 'created_at';
}
$sortDir = strtolower($sortDir) === 'asc' ? 'asc' : 'desc';
return $query->orderBy($sortBy, $sortDir)->paginate($perPage);
}
public function sidebarStats(Customer $customer): VerificationServerSidebarStatsDto
{
return new VerificationServerSidebarStatsDto(
headerMeta: $this->headerMeta($customer),
coverage: $this->coverage($customer),
combinedCapacity: $this->combinedCapacity($customer),
);
}
/**
* Vendor catalog — cross-reference connected types with config('verification.services').
*
* @return array{used_vendor_ids:list<string>,catalog:list<array{id:string,name:string,connected:bool,count:int}>}
*/
private function coverage(Customer $customer): array
{
$rows = \Illuminate\Support\Facades\DB::table('email_verification_servers')
->where('customer_id', $customer->id)
->selectRaw('type, COUNT(*) as cnt')
->groupBy('type')
->pluck('cnt', 'type')
->all();
$services = (array) config('verification.services', []);
$catalog = [];
$usedIds = array_keys($rows);
foreach ($services as $service) {
$id = (string) ($service['id'] ?? '');
if ($id === '') {
continue;
}
$catalog[] = [
'id' => $id,
'name' => (string) ($service['name'] ?? $id),
'connected' => isset($rows[$id]),
'count' => (int) ($rows[$id] ?? 0),
];
}
// Stable sort: connected first, then by name.
usort($catalog, function ($a, $b) {
if ($a['connected'] !== $b['connected']) {
return $a['connected'] ? -1 : 1;
}
return strcasecmp($a['name'], $b['name']);
});
return [
'used_vendor_ids' => $usedIds,
'catalog' => $catalog,
];
}
/**
* Combined throughput across this customer's active verification servers.
* Best-effort — sums options.limit_value normalised to per-hour where possible.
*
* @return array{value:int,unit:string,base:int,formatted:string}
*/
private function combinedCapacity(Customer $customer): array
{
$servers = EmailVerificationServer::query()
->where('customer_id', $customer->id)
->where('status', EmailVerificationServer::STATUS_ACTIVE)
->get(['options']);
$perHour = 0;
$countedAny = false;
foreach ($servers as $server) {
$opts = $server->options;
if (is_string($opts)) {
$opts = json_decode($opts, true) ?: [];
} elseif (! is_array($opts)) {
$opts = [];
}
$value = (int) ($opts['limit_value'] ?? 0);
$base = max(1, (int) ($opts['limit_base'] ?? 1));
$unit = (string) ($opts['limit_unit'] ?? 'hour');
if ($value <= 0) {
continue;
}
$perBase = $value / $base;
$perHourEquivalent = match ($unit) {
'minute' => (int) round($perBase * 60),
'hour' => (int) round($perBase),
'day' => (int) round($perBase / 24),
default => 0,
};
if ($perHourEquivalent > 0) {
$perHour += $perHourEquivalent;
$countedAny = true;
}
}
return [
'value' => $perHour,
'unit' => 'hour',
'base' => 1,
'formatted' => $countedAny
? number_format($perHour) . '/hour'
: '—',
];
}
}