Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.89% covered (success)
91.89%
34 / 37
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
ErrorMessages
91.89% covered (success)
91.89%
34 / 37
0.00% covered (danger)
0.00%
0 / 2
9.04
0.00% covered (danger)
0.00%
0 / 1
 get
96.55% covered (success)
96.55%
28 / 29
0.00% covered (danger)
0.00%
0 / 1
3
 getHighestStatusCode
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
6.56
1<?php
2
3declare(strict_types=1);
4
5namespace BO\Zmscitizenapi\Localization;
6
7use BO\Zmscitizenapi\Middleware\LanguageMiddleware;
8
9class ErrorMessages
10{
11    private const HTTP_OK = 200;
12    private const HTTP_BAD_REQUEST = 400;
13    private const HTTP_FORBIDDEN = 403;
14    private const HTTP_NOT_FOUND = 404;
15    private const HTTP_INVALID_REQUEST_METHOD = 405;
16    private const HTTP_NOT_ACCEPTABLE = 406;
17    private const HTTP_CONFLICT = 409;
18    private const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
19    private const HTTP_TOO_MANY_REQUESTS = 429;
20    private const HTTP_INTERNAL_SERVER_ERROR = 500;
21    private const HTTP_NOT_IMPLEMENTED = 501;
22    private const HTTP_UNAVAILABLE = 503;
23    private const HTTP_UNKNOWN = 520;
24    private const DEFAULT_LANGUAGE = 'DE';
25    private const FALLBACK_LANGUAGE = 'EN';
26// English messages
27    public const EN = [
28        'notImplemented' => [
29            'errorCode' => 'notImplemented',
30            'statusCode' => self::HTTP_NOT_IMPLEMENTED,
31            'errorMessage' => 'Feature not implemented yet.',
32        ],
33        'invalidRequest' => [
34            'errorCode' => 'invalidRequest',
35            'statusCode' => self::HTTP_BAD_REQUEST,
36            'errorMessage' => 'Invalid request.'
37        ],
38        'requestMethodNotAllowed' => [
39            'errorCode' => 'requestMethodNotAllowed',
40            'statusCode' => self::HTTP_INVALID_REQUEST_METHOD,
41            'errorMessage' => 'Request method not allowed.',
42        ],
43        'captchaVerificationFailed' => [
44            'errorCode' => 'captchaVerificationFailed',
45            'statusCode' => self::HTTP_BAD_REQUEST,
46            'errorMessage' => 'Captcha verification failed.'
47        ],
48        'invalidLocationAndServiceCombination' => [
49            'errorCode' => 'invalidLocationAndServiceCombination',
50            'statusCode' => self::HTTP_BAD_REQUEST,
51            'errorMessage' => 'The provided service(s) do not exist at the given location.'
52        ],
53        'invalidStartDate' => [
54            'errorCode' => 'invalidStartDate',
55            'statusCode' => self::HTTP_BAD_REQUEST,
56            'errorMessage' => 'startDate is required and must be a valid date.'
57        ],
58        'invalidEndDate' => [
59            'errorCode' => 'invalidEndDate',
60            'statusCode' => self::HTTP_BAD_REQUEST,
61            'errorMessage' => 'endDate is required and must be a valid date.'
62        ],
63        'invalidOfficeId' => [
64            'errorCode' => 'invalidOfficeId',
65            'statusCode' => self::HTTP_BAD_REQUEST,
66            'errorMessage' => 'officeId should be a 32-bit integer.'
67        ],
68        'invalidServiceId' => [
69            'errorCode' => 'invalidServiceId',
70            'statusCode' => self::HTTP_BAD_REQUEST,
71            'errorMessage' => 'serviceId should be a 32-bit integer.'
72        ],
73        'emptyServiceArrays' => [
74            'errorCode' => 'EMPTY_SERVICE_ARRAYS',
75            'statusCode' => self::HTTP_BAD_REQUEST,
76            'errorMessage' => 'Service IDs and counts cannot be empty'
77        ],
78        'mismatchedArrays' => [
79            'errorCode' => 'MISMATCHED_ARRAYS',
80            'statusCode' => self::HTTP_BAD_REQUEST,
81            'errorMessage' => 'Service IDs and counts must have same length'
82        ],
83        'invalidServiceCount' => [
84            'errorCode' => 'invalidServiceCount',
85            'statusCode' => self::HTTP_BAD_REQUEST,
86            'errorMessage' => 'serviceCounts should be an array of numeric values.'
87        ],
88        'invalidProcessId' => [
89            'errorCode' => 'invalidProcessId',
90            'statusCode' => self::HTTP_BAD_REQUEST,
91            'errorMessage' => 'processId should be a positive 32-bit integer.'
92        ],
93        'invalidScopeId' => [
94            'errorCode' => 'invalidScopeId',
95            'statusCode' => self::HTTP_BAD_REQUEST,
96            'errorMessage' => 'scopeId should be a positive 32-bit integer.'
97        ],
98        'invalidAuthKey' => [
99            'errorCode' => 'invalidAuthKey',
100            'statusCode' => self::HTTP_BAD_REQUEST,
101            'errorMessage' => 'authKey should be a string.'
102        ],
103        'invalidDate' => [
104            'errorCode' => 'invalidDate',
105            'statusCode' => self::HTTP_BAD_REQUEST,
106            'errorMessage' => 'date is required and must be a valid date.'
107        ],
108        'invalidTimestamp' => [
109            'errorCode' => 'invalidTimestamp',
110            'statusCode' => self::HTTP_BAD_REQUEST,
111            'errorMessage' => 'Missing timestamp or invalid timestamp format. It should be a positive numeric value.'
112        ],
113        'invalidFamilyName' => [
114            'errorCode' => 'invalidFamilyName',
115            'statusCode' => self::HTTP_BAD_REQUEST,
116            'errorMessage' => 'familyName should be a non-empty string.'
117        ],
118        'invalidEmail' => [
119            'errorCode' => 'invalidEmail',
120            'statusCode' => self::HTTP_BAD_REQUEST,
121            'errorMessage' => 'email should be a valid email address.'
122        ],
123        'invalidTelephone' => [
124            'errorCode' => 'invalidTelephone',
125            'statusCode' => self::HTTP_BAD_REQUEST,
126            'errorMessage' => 'telephone should be a numeric string between 7 and 15 digits.'
127        ],
128        'invalidCustomTextfield' => [
129            'errorCode' => 'invalidCustomTextfield',
130            'statusCode' => self::HTTP_BAD_REQUEST,
131            'errorMessage' => 'customTextfield should be a string.'
132        ],
133        'appointmentCanNotBeCanceled' => [
134            'errorCode' => 'appointmentCanNotBeCanceled',
135            'statusCode' => self::HTTP_NOT_ACCEPTABLE,
136            'errorMessage' => 'The selected appointment cannot be canceled.'
137        ],
138        'appointmentNotAvailable' => [
139            'errorCode' => 'appointmentNotAvailable',
140            'statusCode' => self::HTTP_NOT_FOUND,
141            'errorMessage' => 'The selected appointment is unfortunately no longer available.'
142        ],
143        'noAppointmentForThisDay' => [
144            'errorCode' => 'noAppointmentForThisDay',
145            'statusCode' => self::HTTP_NOT_FOUND,
146            'errorMessage' => 'No available days found for the given criteria.'
147        ],
148        'captchaVerificationError' => [
149            'errorCode' => 'captchaVerificationError',
150            'statusCode' => self::HTTP_BAD_REQUEST,
151            'errorMessage' => 'An error occurred during captcha verification.'
152        ],
153        'serviceUnavailable' => [
154            'errorCode' => 'serviceUnavailable',
155            'statusCode' => self::HTTP_UNAVAILABLE,
156            'errorMessage' => 'Service Unavailable: The application is under maintenance.'
157        ],
158
159        //Zmsapi exceptions
160        'internalError' => [
161            'errorCode' => 'internalError',
162            'errorMessage' => 'An internal error occurred. Please try again later.',
163            'statusCode' => self::HTTP_INTERNAL_SERVER_ERROR
164        ],
165        'invalidApiClient' => [
166            'errorCode' => 'invalidApiClient',
167            'errorMessage' => 'Invalid API client.',
168            'statusCode' => self::HTTP_BAD_REQUEST
169        ],
170        'sourceNotFound' => [
171            'errorCode' => 'sourceNotFound',
172            'statusCode' => self::HTTP_NOT_FOUND,
173            'errorMessage' => 'Source not found.',
174        ],
175        'departmentNotFound' => [
176            'errorCode' => 'departmentNotFound',
177            'errorMessage' => 'Department not found.',
178            'statusCode' => self::HTTP_NOT_FOUND
179        ],
180        'mailNotFound' => [
181            'errorCode' => 'mailNotFound',
182            'errorMessage' => 'Mail template not found.',
183            'statusCode' => self::HTTP_NOT_FOUND
184        ],
185        'organisationNotFound' => [
186            'errorCode' => 'organisationNotFound',
187            'errorMessage' => 'Organisation not found.',
188            'statusCode' => self::HTTP_NOT_FOUND
189        ],
190        'providerNotFound' => [
191            'errorCode' => 'providerNotFound',
192            'errorMessage' => 'Provider not found.',
193            'statusCode' => self::HTTP_NOT_FOUND
194        ],
195        'requestNotFound' => [
196            'errorCode' => 'requestNotFound',
197            'errorMessage' => 'Requested service not found.',
198            'statusCode' => self::HTTP_NOT_FOUND
199        ],
200        'scopeNotFound' => [
201            'errorCode' => 'scopeNotFound',
202            'errorMessage' => 'Scope not found.',
203            'statusCode' => self::HTTP_NOT_FOUND
204        ],
205        'processInvalid' => [
206            'errorCode' => 'processInvalid',
207            'errorMessage' => 'The process data is invalid.',
208            'statusCode' => self::HTTP_BAD_REQUEST
209        ],
210        'processAlreadyExists' => [
211            'errorCode' => 'processAlreadyExists',
212            'errorMessage' => 'An appointment process already exists.',
213            'statusCode' => self::HTTP_CONFLICT
214        ],
215        'processDeleteFailed' => [
216            'errorCode' => 'processDeleteFailed',
217            'errorMessage' => 'Failed to delete the appointment.',
218            'statusCode' => self::HTTP_INTERNAL_SERVER_ERROR
219        ],
220        'processAlreadyCalled' => [
221            'errorCode' => 'processAlreadyCalled',
222            'errorMessage' => 'The appointment has already been called.',
223            'statusCode' => self::HTTP_CONFLICT
224        ],
225        'processNotReservedAnymore' => [
226            'errorCode' => 'processNotReservedAnymore',
227            'errorMessage' => 'The appointment is no longer reserved.',
228            'statusCode' => self::HTTP_CONFLICT
229        ],
230        'processNotPreconfirmedAnymore' => [
231            'errorCode' => 'processNotPreconfirmedAnymore',
232            'errorMessage' => 'The appointment is no longer preconfirmed.',
233            'statusCode' => self::HTTP_CONFLICT
234        ],
235        'emailIsRequired' => [
236            'errorCode' => 'emailIsRequired',
237            'errorMessage' => 'Email address is required.',
238            'statusCode' => self::HTTP_NOT_ACCEPTABLE
239        ],
240        'telephoneIsRequired' => [
241            'errorCode' => 'telephoneIsRequired',
242            'errorMessage' => 'Telephone number is required.',
243            'statusCode' => self::HTTP_NOT_ACCEPTABLE
244        ],
245        'appointmentNotFound' => [
246            'errorCode' => 'appointmentNotFound',
247            'errorMessage' => 'The requested appointment could not be found.',
248            'statusCode' => self::HTTP_NOT_FOUND
249        ],
250        'authKeyMismatch' => [
251            'errorCode' => 'authKeyMismatch',
252            'errorMessage' => 'Invalid authentication key.',
253            'statusCode' => self::HTTP_NOT_ACCEPTABLE
254        ],
255        'noAppointmentsAtLocation' => [
256            'errorCode' => 'noAppointmentsAtLocation',
257            'errorMessage' => 'No appointments available at the specified location.',
258            'statusCode' => self::HTTP_NOT_FOUND
259        ],
260        'tooManyAppointmentsWithSameMail' => [
261            'errorCode' => 'tooManyAppointmentsWithSameMail',
262            'errorMessage' => 'Too many appointments with the same email address.',
263            'statusCode' => self::HTTP_NOT_ACCEPTABLE
264        ],
265        'scopesNotFound' => [
266            'errorCode' => 'scopesNotFound',
267            'errorMessage' => 'No scopes found.',
268            'statusCode' => self::HTTP_NOT_FOUND
269        ],
270        'preconfirmationExpired' => [
271            'errorCode' => 'preconfirmationExpired',
272            'statusCode' => self::HTTP_BAD_REQUEST,
273            'errorMessage' => 'The preconfirmation has expired. Please make a new appointment.'
274        ],
275
276        //Middleware exceptions
277        'ipBlacklisted' => [
278            'errorCode' => 'IP_BLACKLISTED',
279            'statusCode' => self::HTTP_FORBIDDEN,
280            'errorMessage' => 'Access denied - IP address is blacklisted.'
281        ],
282        'corsOriginNotAllowed' => [
283            'errorCode' => 'corsOriginNotAllowed',
284            'statusCode' => self::HTTP_FORBIDDEN,
285            'errorMessage' => 'Origin not allowed by CORS policy.'
286        ],
287        'csrfTokenMissing' => [
288            'errorCode' => 'csrfTokenMissing',
289            'statusCode' => self::HTTP_FORBIDDEN,
290            'errorMessage' => 'CSRF token is missing.'
291        ],
292        'csrfTokenInvalid' => [
293            'errorCode' => 'csrfTokenInvalid',
294            'statusCode' => self::HTTP_FORBIDDEN,
295            'errorMessage' => 'Invalid CSRF token.'
296        ],
297        'rateLimitExceeded' => [
298            'errorCode' => 'rateLimitExceeded',
299            'statusCode' => self::HTTP_TOO_MANY_REQUESTS,
300            'errorMessage' => 'Rate limit exceeded. Please try again later.'
301        ],
302        'requestEntityTooLarge' => [
303            'errorCode' => 'requestEntityTooLarge',
304            'statusCode' => self::HTTP_REQUEST_ENTITY_TOO_LARGE,
305            'errorMessage' => 'Request entity too large.'
306        ],
307        'securityHeaderViolation' => [
308            'errorCode' => 'securityHeaderViolation',
309            'statusCode' => self::HTTP_FORBIDDEN,
310            'errorMessage' => 'Security policy violation.'
311        ]
312
313    ];
314// German messages
315    public const DE = [
316        'notImplemented' => [
317            'errorCode' => 'notImplemented',
318            'statusCode' => self::HTTP_NOT_IMPLEMENTED,
319            'errorMessage' => 'Funktion ist noch nicht implementiert.',
320        ],
321        'invalidRequest' => [
322            'errorCode' => 'invalidRequest',
323            'statusCode' => self::HTTP_BAD_REQUEST,
324            'errorMessage' => 'Ungültige Anfrage.'
325        ],
326        'requestMethodNotAllowed' => [
327            'errorCode' => 'requestMethodNotAllowed',
328            'statusCode' => self::HTTP_INVALID_REQUEST_METHOD,
329            'errorMessage' => 'Anfragemethode nicht zulässig.',
330        ],
331        'captchaVerificationFailed' => [
332            'errorCode' => 'captchaVerificationFailed',
333            'statusCode' => self::HTTP_BAD_REQUEST,
334            'errorMessage' => 'Captcha-Prüfung fehlgeschlagen.'
335        ],
336        'invalidLocationAndServiceCombination' => [
337            'errorCode' => 'invalidLocationAndServiceCombination',
338            'statusCode' => self::HTTP_BAD_REQUEST,
339            'errorMessage' => 'Die angegebene Dienstleistung ist an diesem Standort nicht verfügbar.'
340        ],
341        'invalidStartDate' => [
342            'errorCode' => 'invalidStartDate',
343            'statusCode' => self::HTTP_BAD_REQUEST,
344            'errorMessage' => 'startDate ist erforderlich und muss ein gültiges Datum sein.'
345        ],
346        'invalidEndDate' => [
347            'errorCode' => 'invalidEndDate',
348            'statusCode' => self::HTTP_BAD_REQUEST,
349            'errorMessage' => 'endDate ist erforderlich und muss ein gültiges Datum sein.'
350        ],
351        'invalidOfficeId' => [
352            'errorCode' => 'invalidOfficeId',
353            'statusCode' => self::HTTP_BAD_REQUEST,
354            'errorMessage' => 'officeId muss eine 32-Bit-Ganzzahl sein.'
355        ],
356        'invalidServiceId' => [
357            'errorCode' => 'invalidServiceId',
358            'statusCode' => self::HTTP_BAD_REQUEST,
359            'errorMessage' => 'serviceId muss eine 32-Bit-Ganzzahl sein.'
360        ],
361        'invalidServiceCount' => [
362            'errorCode' => 'invalidServiceCount',
363            'statusCode' => self::HTTP_BAD_REQUEST,
364            'errorMessage' => 'serviceCounts muss ein Array aus numerischen Werten sein.'
365        ],
366        'emptyServiceArrays' => [
367            'errorCode' => 'EMPTY_SERVICE_ARRAYS',
368            'statusCode' => self::HTTP_BAD_REQUEST,
369            'errorMessage' => 'Service-IDs und Anzahl dürfen nicht leer sein'
370        ],
371        'mismatchedArrays' => [
372            'errorCode' => 'MISMATCHED_ARRAYS',
373            'statusCode' => self::HTTP_BAD_REQUEST,
374            'errorMessage' => 'Service-IDs und Anzahl müssen gleiche Länge haben'
375        ],
376        'invalidProcessId' => [
377            'errorCode' => 'invalidProcessId',
378            'statusCode' => self::HTTP_BAD_REQUEST,
379            'errorMessage' => 'processId muss eine positive 32-Bit-Ganzzahl sein.'
380        ],
381        'invalidScopeId' => [
382            'errorCode' => 'invalidScopeId',
383            'statusCode' => self::HTTP_BAD_REQUEST,
384            'errorMessage' => 'scopeId muss eine positive 32-Bit-Ganzzahl sein.'
385        ],
386        'invalidAuthKey' => [
387            'errorCode' => 'invalidAuthKey',
388            'statusCode' => self::HTTP_BAD_REQUEST,
389            'errorMessage' => 'authKey muss eine Zeichenkette sein.'
390        ],
391        'invalidDate' => [
392            'errorCode' => 'invalidDate',
393            'statusCode' => self::HTTP_BAD_REQUEST,
394            'errorMessage' => 'date ist erforderlich und muss ein gültiges Datum sein.'
395        ],
396        'invalidTimestamp' => [
397            'errorCode' => 'invalidTimestamp',
398            'statusCode' => self::HTTP_BAD_REQUEST,
399            'errorMessage' => 'Fehlender oder ungültiger Zeitstempel. Der Wert muss eine positive Zahl sein.'
400        ],
401        'invalidFamilyName' => [
402            'errorCode' => 'invalidFamilyName',
403            'statusCode' => self::HTTP_BAD_REQUEST,
404            'errorMessage' => 'familyName muss eine nicht-leere Zeichenkette sein.'
405        ],
406        'invalidEmail' => [
407            'errorCode' => 'invalidEmail',
408            'statusCode' => self::HTTP_BAD_REQUEST,
409            'errorMessage' => 'email muss eine gültige E-Mail-Adresse sein.'
410        ],
411        'invalidTelephone' => [
412            'errorCode' => 'invalidTelephone',
413            'statusCode' => self::HTTP_BAD_REQUEST,
414            'errorMessage' => 'telephone muss eine Zahlenkette zwischen 7 und 15 Stellen sein.'
415        ],
416        'invalidCustomTextfield' => [
417            'errorCode' => 'invalidCustomTextfield',
418            'statusCode' => self::HTTP_BAD_REQUEST,
419            'errorMessage' => 'customTextfield muss eine Zeichenkette sein.'
420        ],
421        'appointmentNotAvailable' => [
422            'errorCode' => 'appointmentNotAvailable',
423            'statusCode' => self::HTTP_NOT_FOUND,
424            'errorMessage' => 'Der von Ihnen gewählte Termin ist leider nicht mehr verfügbar.'
425        ],
426        'appointmentCanNotBeCanceled' => [
427            'errorCode' => 'appointmentCanNotBeCanceled',
428            'statusCode' => self::HTTP_NOT_ACCEPTABLE,
429            'errorMessage' => 'Der von Ihnen gewählte Termin kann leider nicht mehr gelöscht werden.'
430        ],
431        'noAppointmentForThisDay' => [
432            'errorCode' => 'noAppointmentForThisDay',
433            'statusCode' => self::HTTP_NOT_FOUND,
434            'errorMessage' => 'Keine verfügbaren Termine für dieses Datum.'
435        ],
436        'captchaVerificationError' => [
437            'errorCode' => 'captchaVerificationError',
438            'statusCode' => self::HTTP_BAD_REQUEST,
439            'errorMessage' => 'Bei der Captcha-Prüfung ist ein Fehler aufgetreten.'
440        ],
441        'serviceUnavailable' => [
442            'errorCode' => 'serviceUnavailable',
443            'statusCode' => self::HTTP_UNAVAILABLE,
444            'errorMessage' => 'Der Dienst ist nicht verfügbar: Die Anwendung wird gerade gewartet.'
445        ],
446
447        //Zmsapi exceptions
448        'internalError' => [
449            'errorCode' => 'internalError',
450            'errorMessage' => 'Ein interner Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.',
451            'statusCode' => self::HTTP_INTERNAL_SERVER_ERROR
452        ],
453        'invalidApiClient' => [
454            'errorCode' => 'invalidApiClient',
455            'errorMessage' => 'Ungültiger API-Client.',
456            'statusCode' => self::HTTP_BAD_REQUEST
457        ],
458        'sourceNotFound' => [
459            'errorCode' => 'sourceNotFound',
460            'statusCode' => self::HTTP_NOT_FOUND,
461            'errorMessage' => 'Quelle nicht gefunden.',
462        ],
463        'departmentNotFound' => [
464            'errorCode' => 'departmentNotFound',
465            'errorMessage' => 'Abteilung nicht gefunden.',
466            'statusCode' => self::HTTP_NOT_FOUND
467        ],
468        'mailNotFound' => [
469            'errorCode' => 'mailNotFound',
470            'errorMessage' => 'E-Mail-Vorlage nicht gefunden.',
471            'statusCode' => self::HTTP_NOT_FOUND
472        ],
473        'organisationNotFound' => [
474            'errorCode' => 'organisationNotFound',
475            'errorMessage' => 'Organisation nicht gefunden.',
476            'statusCode' => self::HTTP_NOT_FOUND
477        ],
478        'providerNotFound' => [
479            'errorCode' => 'providerNotFound',
480            'errorMessage' => 'Anbieter nicht gefunden.',
481            'statusCode' => self::HTTP_NOT_FOUND
482        ],
483        'requestNotFound' => [
484            'errorCode' => 'requestNotFound',
485            'errorMessage' => 'Angeforderter Dienst nicht gefunden.',
486            'statusCode' => self::HTTP_NOT_FOUND
487        ],
488        'scopeNotFound' => [
489            'errorCode' => 'scopeNotFound',
490            'errorMessage' => 'Bereich nicht gefunden.',
491            'statusCode' => self::HTTP_NOT_FOUND
492        ],
493        'processInvalid' => [
494            'errorCode' => 'processInvalid',
495            'errorMessage' => 'Die Prozessdaten sind ungültig.',
496            'statusCode' => self::HTTP_BAD_REQUEST
497        ],
498        'processAlreadyExists' => [
499            'errorCode' => 'processAlreadyExists',
500            'errorMessage' => 'Ein Terminprozess existiert bereits.',
501            'statusCode' => self::HTTP_CONFLICT
502        ],
503        'processDeleteFailed' => [
504            'errorCode' => 'processDeleteFailed',
505            'errorMessage' => 'Der Termin konnte nicht gelöscht werden.',
506            'statusCode' => self::HTTP_INTERNAL_SERVER_ERROR
507        ],
508        'processAlreadyCalled' => [
509            'errorCode' => 'processAlreadyCalled',
510            'errorMessage' => 'Der Termin wurde bereits aufgerufen.',
511            'statusCode' => self::HTTP_CONFLICT
512        ],
513        'processNotReservedAnymore' => [
514            'errorCode' => 'processNotReservedAnymore',
515            'errorMessage' => 'Der Termin ist nicht mehr reserviert.',
516            'statusCode' => self::HTTP_CONFLICT
517        ],
518        'processNotPreconfirmedAnymore' => [
519            'errorCode' => 'processNotPreconfirmedAnymore',
520            'errorMessage' => 'Der Termin ist nicht mehr vorbestätigt.',
521            'statusCode' => self::HTTP_CONFLICT
522        ],
523        'emailIsRequired' => [
524            'errorCode' => 'emailIsRequired',
525            'errorMessage' => 'E-Mail-Adresse ist erforderlich.',
526            'statusCode' => self::HTTP_NOT_ACCEPTABLE
527        ],
528        'telephoneIsRequired' => [
529            'errorCode' => 'telephoneIsRequired',
530            'errorMessage' => 'Telefonnummer ist erforderlich.',
531            'statusCode' => self::HTTP_NOT_ACCEPTABLE
532        ],
533        'appointmentNotFound' => [
534            'errorCode' => 'appointmentNotFound',
535            'errorMessage' => 'Der angeforderte Termin wurde nicht gefunden.',
536            'statusCode' => self::HTTP_NOT_FOUND
537        ],
538        'authKeyMismatch' => [
539            'errorCode' => 'authKeyMismatch',
540            'errorMessage' => 'Ungültiger Authentifizierungsschlüssel.',
541            'statusCode' => self::HTTP_NOT_ACCEPTABLE
542        ],
543        'noAppointmentsAtLocation' => [
544            'errorCode' => 'noAppointmentsAtLocation',
545            'errorMessage' => 'Keine Termine am angegebenen Standort verfügbar.',
546            'statusCode' => self::HTTP_NOT_FOUND
547        ],
548        'tooManyAppointmentsWithSameMail' => [
549            'errorCode' => 'tooManyAppointmentsWithSameMail',
550            'errorMessage' => 'Zu viele Termine mit derselben E-Mail-Adresse.',
551            'statusCode' => self::HTTP_NOT_ACCEPTABLE
552        ],
553        'scopesNotFound' => [
554            'errorCode' => 'scopesNotFound',
555            'errorMessage' => 'Keine Bereiche gefunden.',
556            'statusCode' => self::HTTP_NOT_FOUND
557        ],
558        'preconfirmationExpired' => [
559            'errorCode' => 'preconfirmationExpired',
560            'statusCode' => self::HTTP_BAD_REQUEST,
561            'errorMessage' => 'Die Vorbestätigung ist abgelaufen. Bitte vereinbaren Sie einen neuen Termin.'
562        ],
563
564        //Middleware exceptions
565        'ipBlacklisted' => [
566            'errorCode' => 'IP_BLACKLISTED',
567            'statusCode' => self::HTTP_FORBIDDEN,
568            'errorMessage' => 'Zugriff verweigert - IP-Adresse ist auf der schwarzen Liste.'
569        ],
570        'corsOriginNotAllowed' => [
571            'errorCode' => 'corsOriginNotAllowed',
572            'statusCode' => self::HTTP_FORBIDDEN,
573            'errorMessage' => 'Ursprung durch CORS-Richtlinie nicht erlaubt.'  // DE: 'Ursprung durch CORS-Richtlinie nicht erlaubt.'
574        ],
575        'csrfTokenMissing' => [
576            'errorCode' => 'csrfTokenMissing',
577            'statusCode' => self::HTTP_FORBIDDEN,
578            'errorMessage' => 'CSRF-Token fehlt.'  // DE: 'CSRF-Token fehlt.'
579        ],
580        'csrfTokenInvalid' => [
581            'errorCode' => 'csrfTokenInvalid',
582            'statusCode' => self::HTTP_FORBIDDEN,
583            'errorMessage' => 'Ungültiger CSRF-Token.'  // DE: 'Ungültiger CSRF-Token.'
584        ],
585        'rateLimitExceeded' => [
586            'errorCode' => 'rateLimitExceeded',
587            'statusCode' => self::HTTP_TOO_MANY_REQUESTS,
588            'errorMessage' => 'Anfragelimit überschritten. Bitte versuchen Sie es später erneut.'  // DE: 'Anfragelimit überschritten. Bitte versuchen Sie es später erneut.'
589        ],
590        'requestEntityTooLarge' => [
591            'errorCode' => 'requestEntityTooLarge',
592            'statusCode' => self::HTTP_REQUEST_ENTITY_TOO_LARGE,
593            'errorMessage' => 'Anfrage zu groß.'  // DE: 'Anfrage zu groß.'
594        ],
595        'securityHeaderViolation' => [
596            'errorCode' => 'securityHeaderViolation',
597            'statusCode' => self::HTTP_FORBIDDEN,
598            'errorMessage' => 'Verstoß gegen Sicherheitsrichtlinien.'  // DE: 'Verstoß gegen Sicherheitsrichtlinien.'
599        ]
600
601    ];
602    public const UA = [
603        'notImplemented' => [
604            'errorCode' => 'notImplemented',
605            'statusCode' => self::HTTP_NOT_IMPLEMENTED,
606            'errorMessage' => 'Функцію ще не реалізовано.',
607        ],
608        'invalidRequest' => [
609            'errorCode' => 'invalidRequest',
610            'statusCode' => self::HTTP_BAD_REQUEST,
611            'errorMessage' => 'Недійсний запит.'
612        ],
613        'requestMethodNotAllowed' => [
614            'errorCode' => 'requestMethodNotAllowed',
615            'statusCode' => self::HTTP_INVALID_REQUEST_METHOD,
616            'errorMessage' => 'Метод запиту не дозволено.',
617        ],
618        'captchaVerificationFailed' => [
619            'errorCode' => 'captchaVerificationFailed',
620            'statusCode' => self::HTTP_BAD_REQUEST,
621            'errorMessage' => 'Перевірка капчі не вдалася.'
622        ],
623        'invalidLocationAndServiceCombination' => [
624            'errorCode' => 'invalidLocationAndServiceCombination',
625            'statusCode' => self::HTTP_BAD_REQUEST,
626            'errorMessage' => 'Вказані послуги не існують у цьому місці.'
627        ],
628        'invalidStartDate' => [
629            'errorCode' => 'invalidStartDate',
630            'statusCode' => self::HTTP_BAD_REQUEST,
631            'errorMessage' => 'Потрібна дійсна дата початку.'
632        ],
633        'invalidEndDate' => [
634            'errorCode' => 'invalidEndDate',
635            'statusCode' => self::HTTP_BAD_REQUEST,
636            'errorMessage' => 'Потрібна дійсна дата завершення.'
637        ],
638        'invalidOfficeId' => [
639            'errorCode' => 'invalidOfficeId',
640            'statusCode' => self::HTTP_BAD_REQUEST,
641            'errorMessage' => 'officeId має бути 32-бітним цілим числом.'
642        ],
643        'invalidServiceId' => [
644            'errorCode' => 'invalidServiceId',
645            'statusCode' => self::HTTP_BAD_REQUEST,
646            'errorMessage' => 'serviceId має бути 32-бітним цілим числом.'
647        ],
648        'emptyServiceArrays' => [
649            'errorCode' => 'EMPTY_SERVICE_ARRAYS',
650            'statusCode' => self::HTTP_BAD_REQUEST,
651            'errorMessage' => 'Ідентифікатори та кількість послуг не можуть бути порожніми'
652        ],
653        'mismatchedArrays' => [
654            'errorCode' => 'MISMATCHED_ARRAYS',
655            'statusCode' => self::HTTP_BAD_REQUEST,
656            'errorMessage' => 'Ідентифікатори та кількість послуг повинні мати однакову довжину'
657        ],
658        'invalidServiceCount' => [
659            'errorCode' => 'invalidServiceCount',
660            'statusCode' => self::HTTP_BAD_REQUEST,
661            'errorMessage' => 'serviceCounts має бути масивом числових значень.'
662        ],
663        'invalidProcessId' => [
664            'errorCode' => 'invalidProcessId',
665            'statusCode' => self::HTTP_BAD_REQUEST,
666            'errorMessage' => 'processId має бути додатним 32-бітним цілим числом.'
667        ],
668        'invalidScopeId' => [
669            'errorCode' => 'invalidScopeId',
670            'statusCode' => self::HTTP_BAD_REQUEST,
671            'errorMessage' => 'scopeId має бути додатним 32-бітним цілим числом.'
672        ],
673        'invalidAuthKey' => [
674            'errorCode' => 'invalidAuthKey',
675            'statusCode' => self::HTTP_BAD_REQUEST,
676            'errorMessage' => 'authKey має бути рядком.'
677        ],
678        'invalidDate' => [
679            'errorCode' => 'invalidDate',
680            'statusCode' => self::HTTP_BAD_REQUEST,
681            'errorMessage' => 'Потрібна дійсна дата.'
682        ],
683        'invalidTimestamp' => [
684            'errorCode' => 'invalidTimestamp',
685            'statusCode' => self::HTTP_BAD_REQUEST,
686            'errorMessage' => 'Відсутня або недійсна мітка часу. Має бути додатним числовим значенням.'
687        ],
688        'invalidFamilyName' => [
689            'errorCode' => 'invalidFamilyName',
690            'statusCode' => self::HTTP_BAD_REQUEST,
691            'errorMessage' => 'Прізвище не може бути порожнім.'
692        ],
693        'invalidEmail' => [
694            'errorCode' => 'invalidEmail',
695            'statusCode' => self::HTTP_BAD_REQUEST,
696            'errorMessage' => 'Потрібна дійсна електронна адреса.'
697        ],
698        'invalidTelephone' => [
699            'errorCode' => 'invalidTelephone',
700            'statusCode' => self::HTTP_BAD_REQUEST,
701            'errorMessage' => 'Номер телефону має містити від 7 до 15 цифр.'
702        ],
703        'invalidCustomTextfield' => [
704            'errorCode' => 'invalidCustomTextfield',
705            'statusCode' => self::HTTP_BAD_REQUEST,
706            'errorMessage' => 'customTextfield має бути рядком.'
707        ],
708        'appointmentCanNotBeCanceled' => [
709            'errorCode' => 'appointmentCanNotBeCanceled',
710            'statusCode' => self::HTTP_NOT_ACCEPTABLE,
711            'errorMessage' => 'Обраний запис не може бути скасовано.'
712        ],
713        'appointmentNotAvailable' => [
714            'errorCode' => 'appointmentNotAvailable',
715            'statusCode' => self::HTTP_NOT_FOUND,
716            'errorMessage' => 'На жаль, обраний запис більше недоступний.'
717        ],
718        'noAppointmentForThisDay' => [
719            'errorCode' => 'noAppointmentForThisDay',
720            'statusCode' => self::HTTP_NOT_FOUND,
721            'errorMessage' => 'Немає доступних днів за вказаними критеріями.'
722        ],
723        'captchaVerificationError' => [
724            'errorCode' => 'captchaVerificationError',
725            'statusCode' => self::HTTP_BAD_REQUEST,
726            'errorMessage' => 'Виникла помилка під час перевірки капчі.'
727        ],
728        'serviceUnavailable' => [
729            'errorCode' => 'serviceUnavailable',
730            'statusCode' => self::HTTP_UNAVAILABLE,
731            'errorMessage' => 'Сервіс недоступний: Додаток перебуває на технічному обслуговуванні.'
732        ],
733        'internalError' => [
734            'errorCode' => 'internalError',
735            'errorMessage' => 'Виникла внутрішня помилка. Спробуйте пізніше.',
736            'statusCode' => self::HTTP_INTERNAL_SERVER_ERROR
737        ],
738        'invalidApiClient' => [
739            'errorCode' => 'invalidApiClient',
740            'errorMessage' => 'Недійсний API клієнт.',
741            'statusCode' => self::HTTP_BAD_REQUEST
742        ],
743        'sourceNotFound' => [
744            'errorCode' => 'sourceNotFound',
745            'statusCode' => self::HTTP_NOT_FOUND,
746            'errorMessage' => 'Джерело не знайдено.',
747        ],
748        'departmentNotFound' => [
749            'errorCode' => 'departmentNotFound',
750            'errorMessage' => 'Відділ не знайдено.',
751            'statusCode' => self::HTTP_NOT_FOUND
752        ],
753        'mailNotFound' => [
754            'errorCode' => 'mailNotFound',
755            'errorMessage' => 'Шаблон листа не знайдено.',
756            'statusCode' => self::HTTP_NOT_FOUND
757        ],
758        'organisationNotFound' => [
759            'errorCode' => 'organisationNotFound',
760            'errorMessage' => 'Організацію не знайдено.',
761            'statusCode' => self::HTTP_NOT_FOUND
762        ],
763        'providerNotFound' => [
764            'errorCode' => 'providerNotFound',
765            'errorMessage' => 'Постачальника не знайдено.',
766            'statusCode' => self::HTTP_NOT_FOUND
767        ],
768        'requestNotFound' => [
769            'errorCode' => 'requestNotFound',
770            'errorMessage' => 'Запитану послугу не знайдено.',
771            'statusCode' => self::HTTP_NOT_FOUND
772        ],
773        'scopeNotFound' => [
774            'errorCode' => 'scopeNotFound',
775            'errorMessage' => 'Область не знайдено.',
776            'statusCode' => self::HTTP_NOT_FOUND
777        ],
778        'processInvalid' => [
779            'errorCode' => 'processInvalid',
780            'errorMessage' => 'Дані процесу недійсні.',
781            'statusCode' => self::HTTP_BAD_REQUEST
782        ],
783        'processAlreadyExists' => [
784            'errorCode' => 'processAlreadyExists',
785            'errorMessage' => 'Процес запису вже існує.',
786            'statusCode' => self::HTTP_CONFLICT
787        ],
788        'processDeleteFailed' => [
789            'errorCode' => 'processDeleteFailed',
790            'errorMessage' => 'Не вдалося видалити запис.',
791            'statusCode' => self::HTTP_INTERNAL_SERVER_ERROR
792        ],
793        'processAlreadyCalled' => [
794            'errorCode' => 'processAlreadyCalled',
795            'errorMessage' => 'Запис вже викликано.',
796            'statusCode' => self::HTTP_CONFLICT
797        ],
798        'processNotReservedAnymore' => [
799            'errorCode' => 'processNotReservedAnymore',
800            'errorMessage' => 'Запис більше не зарезервовано.',
801            'statusCode' => self::HTTP_CONFLICT
802        ],
803        'processNotPreconfirmedAnymore' => [
804            'errorCode' => 'processNotPreconfirmedAnymore',
805            'errorMessage' => 'Запис більше не попередньо підтверджено.',
806            'statusCode' => self::HTTP_CONFLICT
807        ],
808        'emailIsRequired' => [
809            'errorCode' => 'emailIsRequired',
810            'errorMessage' => 'Потрібна електронна адреса.',
811            'statusCode' => self::HTTP_NOT_ACCEPTABLE
812        ],
813        'telephoneIsRequired' => [
814            'errorCode' => 'telephoneIsRequired',
815            'errorMessage' => 'Потрібен номер телефону.',
816            'statusCode' => self::HTTP_NOT_ACCEPTABLE
817        ],
818        'appointmentNotFound' => [
819            'errorCode' => 'appointmentNotFound',
820            'errorMessage' => 'Запитаний запис не знайдено.',
821            'statusCode' => self::HTTP_NOT_FOUND
822        ],
823        'authKeyMismatch' => [
824            'errorCode' => 'authKeyMismatch',
825            'errorMessage' => 'Недійсний ключ автентифікації.',
826            'statusCode' => self::HTTP_NOT_ACCEPTABLE
827        ],
828        'noAppointmentsAtLocation' => [
829            'errorCode' => 'noAppointmentsAtLocation',
830            'errorMessage' => 'Немає доступних записів у вказаному місці.',
831            'statusCode' => self::HTTP_NOT_FOUND
832        ],
833        'tooManyAppointmentsWithSameMail' => [
834            'errorCode' => 'tooManyAppointmentsWithSameMail',
835            'errorMessage' => 'Забагато записів з однією електронною адресою.',
836            'statusCode' => self::HTTP_NOT_ACCEPTABLE
837        ],
838        'scopesNotFound' => [
839            'errorCode' => 'scopesNotFound',
840            'errorMessage' => 'Області не знайдено.',
841            'statusCode' => self::HTTP_NOT_FOUND
842        ],
843        'preconfirmationExpired' => [
844            'errorCode' => 'preconfirmationExpired',
845            'statusCode' => self::HTTP_BAD_REQUEST,
846            'errorMessage' => 'Попереднє підтвердження закінчилося. Будь ласка, створіть новий запис.'
847        ],
848        'ipBlacklisted' => [
849            'errorCode' => 'IP_BLACKLISTED',
850            'statusCode' => self::HTTP_FORBIDDEN,
851            'errorMessage' => 'Доступ заборонено - IP-адресу заблоковано.'
852        ],
853        'corsOriginNotAllowed' => [
854            'errorCode' => 'corsOriginNotAllowed',
855            'statusCode' => self::HTTP_FORBIDDEN,
856            'errorMessage' => 'Джерело не дозволено політикою CORS.'
857        ],
858        'csrfTokenMissing' => [
859            'errorCode' => 'csrfTokenMissing',
860            'statusCode' => self::HTTP_FORBIDDEN,
861            'errorMessage' => 'Відсутній CSRF токен.'
862        ],
863        'csrfTokenInvalid' => [
864            'errorCode' => 'csrfTokenInvalid',
865            'statusCode' => self::HTTP_FORBIDDEN,
866            'errorMessage' => 'Недійсний CSRF токен.'
867        ],
868        'rateLimitExceeded' => [
869            'errorCode' => 'rateLimitExceeded',
870            'statusCode' => self::HTTP_TOO_MANY_REQUESTS,
871            'errorMessage' => 'Перевищено ліміт запитів. Спробуйте пізніше.'
872        ],
873        'requestEntityTooLarge' => [
874            'errorCode' => 'requestEntityTooLarge',
875            'statusCode' => self::HTTP_REQUEST_ENTITY_TOO_LARGE,
876            'errorMessage' => 'Тіло запиту занадто велике.'
877        ],
878        'securityHeaderViolation' => [
879            'errorCode' => 'securityHeaderViolation',
880            'statusCode' => self::HTTP_FORBIDDEN,
881            'errorMessage' => 'Порушення політики безпеки.'
882        ]
883    ];
884/**
885     * Get an error message by key with fallback logic.
886     *
887     * @param string $key The error message key.
888     * @param string|null $language Optional language (default is LanguageMiddleware's default).
889     * @return array The error message array.
890     */
891    public static function get(string $key, ?string $language = null): array
892    {
893        $language = LanguageMiddleware::normalizeLanguage($language);
894        $messages = match ($language) {
895            'en' => self::EN,
896            'de' => self::DE,
897            'ua' => self::UA,
898            default => constant('self::' . strtoupper(LanguageMiddleware::getDefaultLanguage())),
899        };
900        if (isset($messages[$key])) {
901            return $messages[$key];
902        }
903
904        $fallbackMessages = constant('self::' . strtoupper(LanguageMiddleware::getFallbackLanguage()));
905        if (isset($fallbackMessages[$key])) {
906            return $fallbackMessages[$key];
907        }
908
909        return match ($language) {
910            'de' => [
911                'errorCode' => 'unknownError',
912                'statusCode' => self::HTTP_UNKNOWN,
913                'errorMessage' => 'Ein unbekannter Fehler ist aufgetreten.'
914            ],
915            'ua' => [
916                'errorCode' => 'unknownError',
917                'statusCode' => self::HTTP_UNKNOWN,
918                'errorMessage' => 'Виникла невідома помилка.'
919            ],
920            default => [
921                'errorCode' => 'unknownError',
922                'statusCode' => self::HTTP_UNKNOWN,
923                'errorMessage' => 'An unknown error occurred.'
924            ],
925        };
926    }
927
928    /**
929     * Get the highest status code from an array of errors.
930     *
931     * @param array $errors Array of error messages
932     * @return int The highest status code found, or HTTP_OK (200) if no errors
933     * @throws \InvalidArgumentException If any error has an invalid structure
934     */
935    public static function getHighestStatusCode(array $errors): int
936    {
937        if (empty($errors)) {
938            return self::HTTP_OK;
939        }
940
941        $errorCodes = [];
942        foreach ($errors as $error) {
943            if (!is_array($error) || !isset($error['statusCode']) || !is_int($error['statusCode'])) {
944                throw new \InvalidArgumentException('Invalid error structure. Each error must have a statusCode.');
945            }
946            $errorCodes[] = $error['statusCode'];
947        }
948
949        return max($errorCodes);
950    }
951}