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