Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 52
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
RecordAnalyticsFactAction
0.00% covered (danger)
0.00%
0 / 52
0.00% covered (danger)
0.00%
0 / 3
210
0.00% covered (danger)
0.00%
0 / 1
 execute
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
6
 bumpDailyMetrics
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
72
 incrementFor
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Analytics\Application\Actions;
6
7use Illuminate\Support\Carbon;
8use Illuminate\Support\Facades\Event;
9use ImovelZapAi\Analytics\Domain\Enums\AnalyticsEventKey;
10use ImovelZapAi\Analytics\Domain\Enums\MetricKey;
11use ImovelZapAi\Analytics\Domain\Events\AnalyticsFactRecorded;
12use ImovelZapAi\Analytics\Infrastructure\Persistence\Eloquent\AnalyticsDailyMetricModel;
13use ImovelZapAi\Analytics\Infrastructure\Persistence\Eloquent\AnalyticsFactModel;
14
15final class RecordAnalyticsFactAction
16{
17    public function execute(
18        string $tenantId,
19        AnalyticsEventKey $eventKey,
20        ?Carbon $occurredAt = null,
21        ?string $entityType = null,
22        ?string $entityId = null,
23        ?int $userId = null,
24        ?string $amount = null,
25        array $meta = [],
26    ): AnalyticsFactModel {
27        $occurredAt ??= now();
28
29        $fact = AnalyticsFactModel::query()->create([
30            'tenant_id' => $tenantId,
31            'event_key' => $eventKey->value,
32            'occurred_at' => $occurredAt,
33            'entity_type' => $entityType,
34            'entity_id' => $entityId,
35            'user_id' => $userId,
36            'amount' => $amount,
37            'meta' => $meta === [] ? null : $meta,
38            'created_at' => now(),
39        ]);
40
41        $this->bumpDailyMetrics($tenantId, $eventKey, $occurredAt, $userId, $amount, $meta);
42
43        Event::dispatch(AnalyticsFactRecorded::fromModel($fact));
44
45        return $fact;
46    }
47
48    /** @param array<string, mixed> $meta */
49    private function bumpDailyMetrics(
50        string $tenantId,
51        AnalyticsEventKey $eventKey,
52        Carbon $occurredAt,
53        ?int $userId,
54        ?string $amount,
55        array $meta,
56    ): void {
57        $metricDate = $occurredAt->toDateString();
58        $dimensions = [null];
59
60        if ($userId !== null) {
61            $dimensions[] = 'user:'.$userId;
62        }
63
64        $source = $meta['source'] ?? null;
65        if (is_string($source) && $source !== '') {
66            $dimensions[] = 'channel:'.$source;
67        }
68
69        foreach ($eventKey->metricKeys() as $metricKey) {
70            foreach (array_unique($dimensions) as $dimensionKey) {
71                $increment = $this->incrementFor($metricKey, $amount, $meta);
72                if ($increment <= 0) {
73                    continue;
74                }
75
76                $existing = AnalyticsDailyMetricModel::query()
77                    ->where('tenant_id', $tenantId)
78                    ->whereDate('metric_date', $metricDate)
79                    ->where('metric_key', $metricKey->value)
80                    ->when(
81                        $dimensionKey === null,
82                        fn ($q) => $q->whereNull('dimension_key'),
83                        fn ($q) => $q->where('dimension_key', $dimensionKey),
84                    )
85                    ->first();
86
87                if ($existing) {
88                    $existing->update([
89                        'value' => bcadd((string) $existing->value, (string) $increment, 2),
90                    ]);
91                } else {
92                    AnalyticsDailyMetricModel::query()->create([
93                        'tenant_id' => $tenantId,
94                        'metric_date' => $metricDate,
95                        'metric_key' => $metricKey->value,
96                        'dimension_key' => $dimensionKey,
97                        'value' => $increment,
98                    ]);
99                }
100            }
101        }
102    }
103
104    /** @param array<string, mixed> $meta */
105    private function incrementFor(MetricKey $metricKey, ?string $amount, array $meta): float
106    {
107        if ($metricKey === MetricKey::VolumeNegotiated || $metricKey === MetricKey::PaymentsReceivedAmount) {
108            $value = $amount ?? ($meta['amount'] ?? null);
109
110            return $value !== null ? (float) $value : 0.0;
111        }
112
113        return 1.0;
114    }
115}