PropertyService::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 0
c 1
b 0
f 0
dl 0
loc 8
ccs 0
cts 2
cp 0
rs 10
cc 1
nc 1
nop 6
crap 2
1
<?php
2
3
namespace App\Service;
4
5
use App\Client\GmailApiClient;
6
use App\DataProvider\EmailTemplateProvider;
7
use App\DTO\Property;
8
use App\Entity\User;
9
use App\Exception\GmailApiException;
10
use App\Exception\GoogleException;
11
use App\Exception\GoogleInsufficientPermissionException;
12
use App\Exception\GoogleRefreshTokenException;
13
use App\Exception\ParseException;
14
use App\Exception\ParserNotFoundException;
15
use App\Parser\ParserLocator;
16
use Exception;
17
use Psr\Log\LoggerInterface;
18
use function array_merge;
19
20
class PropertyService
21
{
22
    private const ORDER_ASC = 1;
23
24
    public function __construct(
25
        private GmailApiClient $gmailApiClient,
26
        private GmailMessageService $gmailMessageService,
27
        private GoogleOAuthService $googleOAuthService,
28
        private ParserLocator $parserLocator,
29
        private EmailTemplateProvider $emailTemplateProvider,
30
        private LoggerInterface $logger
31
    ) {}
32
33
    /**
34
     * @return Property[]
35
     *
36
     * @throws GmailApiException|GoogleException|GoogleRefreshTokenException|GoogleInsufficientPermissionException|ParserNotFoundException
37
     */
38
    public function find(User $user, array $criteria, array $sort): array
39
    {
40
        $properties = [];
41
42
        // Refresh the access token if expired. Otherwise, get the current access token
43
        $accessToken = $this->googleOAuthService->refreshAccessTokenIfExpired($user);
44
45
        // Fetch the Gmail messages IDs matching the user criteria
46
        $messages = $this->gmailApiClient->getMessages($criteria, $accessToken);
47
48
        foreach ($messages as $message) {
49
50
            try {
51
                $message = $this->gmailApiClient->getMessage($message->id);
52
            } catch (Exception $e) {
53
                $this->logger->error('Error while retrieving a message: ' . $e->getMessage(), [
54
                    'message_id' => $message->id,
55
                ]);
56
57
                continue;
58
            }
59
60
            $createdAt = $this->gmailMessageService->getCreatedAt($message);
61
            $headers = $this->gmailMessageService->getHeaders($message);
62
            $html = $this->gmailMessageService->getHtml($message);
63
64
            // For logging needs
65
            $context = array_merge($headers, ['created_at' => $createdAt]);
66
67
            // Find the email template matching the email headers
68
            $emailTemplate = $this->emailTemplateProvider->find($headers['from'], $headers['subject']);
69
70
            if (null === $emailTemplate) {
71
                $this->logger->error('No email template found', $context);
72
73
                continue;
74
            }
75
76
            // Parse the HTML content to extract properties
77
            try {
78
                $properties[] = $this->parserLocator->get($emailTemplate->getName())->parse($html, $criteria, [
79
                    'email_template' => $emailTemplate->getName(),
80
                    'date' => $createdAt
81
                ]);
82
            } catch (ParseException $e) {
83
                $this->logger->error($e->getMessage(), array_merge($context, ['email_template' => $emailTemplate->getName()]));
84
85
                continue;
86
            }
87
        }
88
89
        // Flatten the array
90
        $properties = array_merge(...$properties);
91
92
        // Remove duplicates from same provider
93
        $this->removeDuplicates($properties);
94
95
        // Group property ads by property
96
        $this->groupPropertyAds($properties);
97
98
        // Sort properties
99
        $this->sort($properties, $sort[0], $sort[1]);
100
101
        return $properties;
102
    }
103
104
    /**
105
     * @param Property[] $properties
106
     */
107
    private function removeDuplicates(array &$properties): void
108
    {
109
        foreach ($properties as $i => $newestProperty) {
110
            foreach ($properties as $oldestProperty) {
111
                if (
112
                    $oldestProperty !== $newestProperty &&
113
                    $oldestProperty->getAd()->getProvider() === $newestProperty->getAd()->getProvider() &&
114
                    $oldestProperty->equals($newestProperty)
115
                ) {
116
                    unset($properties[$i]);
117
                }
118
            }
119
        }
120
    }
121
122
    /**
123
     * @param Property[] $properties
124
     */
125
    private function groupPropertyAds(array &$properties): void
126
    {
127
        foreach ($properties as $i => $newestProperty) {
128
            foreach ($properties as $oldestProperty) {
129
                if ($oldestProperty !== $newestProperty && $oldestProperty->equals($newestProperty)) {
130
                    $oldestProperty->addAd($newestProperty->getAd());
131
                    unset($properties[$i]);
132
                }
133
            }
134
        }
135
    }
136
137
    /**
138
     * @param Property[] $properties
139
     */
140
    private function sort(array &$properties, string $field, int $order = self::ORDER_ASC): void
141
    {
142
        $getter = 'get' . ucfirst($field);
143
144
        if (!method_exists(Property::class, $getter)) {
145
            return;
146
        }
147
148
        usort($properties, static function (Property $p1, Property $p2) use ($getter, $order) {
149
            $comparison = $p1->{$getter}() <=> $p2->{$getter}();
150
151
            return self::ORDER_ASC === $order ? $comparison : -$comparison;
152
        });
153
    }
154
}
155