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