src/Model/EwsNotificationModel.php line 31

Open in your IDE?
  1. <?php
  2. namespace App\Model;
  3. use DateTime;
  4. use Pimcore\Db;
  5. use DOMDocument;
  6. use SimpleXMLElement;
  7. use Pimcore\Model\Asset;
  8. use Doctrine\DBAL\Connection;
  9. use Pimcore\Model\DataObject;
  10. use Symfony\Component\Uid\UuidV4;
  11. use Pimcore\Model\DataObject\Region;
  12. use Pimcore\Model\DataObject\AlertType;
  13. use Pimcore\Model\DataObject\EwsNotification;
  14. use Symfony\Component\HttpFoundation\Request;
  15. use Pimcore\Model\DataObject\FetchSentEwsEmail;
  16. use App\C2IntegrationBundle\Service\C2Service;
  17. use Pimcore\Model\Asset\MetaData\ClassDefinition\Data\DataObject as DataDataObject;
  18. use Pimcore\Model\DataObject\EwsAndReportUserGroup;
  19. use App\Model\UserModel;
  20. use Symfony\Component\Process\Process;
  21. use Symfony\Component\Process\Exception\ProcessFailedException;
  22. class EwsNotificationModel
  23. {
  24.     public $c2Service;
  25.     private $userModel;
  26.     function __construct()
  27.     {
  28.         $this->c2Service = new C2Service();
  29.         $this->userModel = new UserModel();
  30.     }
  31.     public function createNotification($request$params$userInterface$translator): array
  32.     {
  33.         $result = [];
  34.         // try {
  35.         $regionId $params['regionId'] ?? null;
  36.         $governorateIds $params['governateIds'] ?? null;
  37.         $municipalities $params['municipalityIds'] ?? null;
  38.         $ewsOtherLocationIds $params['ewsOtherLocationIds'] ?? null;
  39.         $alertActionId $params['alertActionId'] ?? null;
  40.         $alertTypeId $params['alertTypeId'] ?? null;
  41.         $weatherPhenomenonId $params['weatherPhenomenonId'] ?? null;
  42.         $coordinates json_encode($params['coordinates'] ?? []);
  43.         $message $params['message'] ?? [];
  44.         $alertHazardId $params['alertHazardId'] ?? null;
  45.         $otherLocationId $params['otherLocationId'] ?? null;
  46.         $enableTwitterNotification $params['enableTwitterNotification'] ?? false;
  47.         $enableSMSNotification $params['enableSMSNotification'] ?? false;
  48.         $enableEmailNotification $params['enableEmailNotification'] ?? false;
  49.         $x_post $params['x_post'] ?? "";
  50.         $previewText = isset($params['previewText']) ? $params['previewText'] : false;
  51.         $ewsNotification = new DataObject\EwsNotification();
  52.         $ewsNotification->setParent(DataObject\Service::createFolderByPath('/Notification/EWSNotification'));
  53.         $ewsNotification->setKey(md5($coordinates $regionId $governorateIds) . '-' time());
  54.         $ewsNotification->setGuid(\App\Lib\Utility::getUUID()->toRfc4122());
  55.         if (isset($params['startDate'])) {
  56.             $startTime $params['startTime'] ?? '00:00:00';
  57.             $fromDate $params['startDate'] . ' ' $startTime;
  58.             $ewsNotification->setStartDate(\Carbon\Carbon::createFromFormat('Y-m-d H:i:s'$fromDate));
  59.         }
  60.         if (isset($params['endDate'])) {
  61.             $endTime $params['endTime'] ?? '00:00:00';
  62.             $endDate $params['endDate'] . ' ' $endTime;
  63.             $ewsNotification->setEndDate(\Carbon\Carbon::createFromFormat('Y-m-d H:i:s'$endDate));
  64.         }
  65.         if (isset($message['en'])) {
  66.             $ewsNotification->setMessage($message['en'], 'en');
  67.         }
  68.         if (isset($message['ar'])) {
  69.             $ewsNotification->setMessage($message['ar'], 'ar');
  70.         }
  71.         if (!empty($regionId)) {
  72.             $regionObj DataObject\Region::getByRegionId($regionId1);
  73.             if (!empty($regionObj)) {
  74.                 $ewsNotification->setRegion($regionObj);
  75.                 if ($governorateIds) {
  76.                     $governorateIdArr explode(","$governorateIds);
  77.                     if ($governorateIdArr) {
  78.                         $applyGovernates = [];
  79.                         foreach ($governorateIdArr as $id) {
  80.                             if(!empty($id)){
  81.                                 $applyGovernates[] = \Pimcore\Model\DataObject\Governorate::getByGovernoteId($idtrue);
  82.                                 if (!empty(array_unique($applyGovernates))) {
  83.                                     $ewsNotification->setGovernorate(array_unique($applyGovernates));
  84.                                 }
  85.                             }
  86.                         }
  87.                     }
  88.                     if ($municipalities) {
  89.                         $municipalitiesIdArr explode(","$municipalities);
  90.                         if ($municipalitiesIdArr) {
  91.                             $applyMunicipalities = [];
  92.                             foreach ($municipalitiesIdArr as $id) {
  93.                                 $applyMunicipalities[] = \Pimcore\Model\DataObject\Municipality::getByMunicipalityid($idtrue);
  94.                                 if (!empty(array_unique($applyMunicipalities))) {
  95.                                     $ewsNotification->setMunicipality(array_unique($applyMunicipalities));
  96.                                 }
  97.                             }
  98.                         }
  99.                     }
  100.                 } else {
  101.                     $ewsNotification->setGovernorate($this->governorateByRegion($regionObj->getId()));
  102.                 }
  103.             }
  104.         }
  105.         if ($ewsOtherLocationIds) {
  106.             $ewsOtherLocationIdArr explode(","$ewsOtherLocationIds);
  107.             if ($ewsOtherLocationIdArr) {
  108.                 $applyOtherGovernates = [];
  109.                 foreach ($ewsOtherLocationIdArr as $id) {
  110.                     $applyOtherGovernates[] = \Pimcore\Model\DataObject\EwsOtherLocation::getByGovernoteId($idtrue);
  111.                     if (!empty(array_unique($applyOtherGovernates))) {
  112.                         $ewsNotification->setEwsOtherLocations(array_unique($applyOtherGovernates));
  113.                     }
  114.                 }
  115.             }
  116.         }
  117.         if (!empty($alertHazardId)) {
  118.             $alertHazardArr explode(","$alertHazardId);
  119.             if ($alertHazardArr) {
  120.                 $alertHazardArrRequest = [];
  121.                 foreach ($alertHazardArr as $alertHazardId) {
  122.                     $weatherPhenomenonAffect DataObject\AlertHazard::getByAlertHazardId($alertHazardId1);
  123.                     if ($weatherPhenomenonAffect) {
  124.                         $alertHazardArrRequest[] = $weatherPhenomenonAffect;
  125.                     }
  126.                 }
  127.                 $ewsNotification->setAlertHazard($alertHazardArrRequest);
  128.             }
  129.         }
  130.         if (!empty($alertTypeId)) {
  131.             $alertType DataObject\AlertType::getByAlertTypeId($alertTypeId1);
  132.             $ewsNotification->setAlertType($alertType);
  133.         }
  134.         if (!empty($alertActionId)) {
  135.             $alertActionArr explode(","$alertActionId);
  136.             if ($alertActionArr) {
  137.                 $alertActionArrRequest = [];
  138.                 foreach ($alertActionArr as $alertActionId) {
  139.                     $alertAction DataObject\AlertAction::getByAlertActionId($alertActionId1);
  140.                     if ($alertAction) {
  141.                         $alertActionArrRequest[] = $alertAction;
  142.                     }
  143.                 }
  144.                 $ewsNotification->setAlertAction($alertActionArrRequest);
  145.             }
  146.         }
  147.         if (!empty($otherLocationId)) {
  148.             $otherLocationArr explode(","$otherLocationId);
  149.             if ($otherLocationArr) {
  150.                 $otherLocationArrRequest = [];
  151.                 foreach ($otherLocationArr as $otherLocationId) {
  152.                     $otherLocation DataObject\OtherLocation::getByOtherLocationId($otherLocationId1);
  153.                     if ($otherLocation) {
  154.                         $otherLocationArrRequest[] = $otherLocation;
  155.                     }
  156.                 }
  157.                 $ewsNotification->setOtherLocations($otherLocationArrRequest);
  158.             }
  159.         }
  160.         if (!empty($weatherPhenomenonId)) {
  161.             $alertStatus DataObject\AlertStatus::getByAlertStatusId($weatherPhenomenonIdtrue);
  162.             $ewsNotification->setAlertStatus($alertStatus);
  163.         }
  164.         $ewsNotification->setCoordinates($coordinates);
  165.         $addressComponents $params['address_components'] ?? [];
  166.         $this->addAddressComponentsFieldCollection($addressComponents$ewsNotification);
  167.         if (!empty($params['file'])) {
  168.             $asset $this->createAsset($params['file'], time() . $params['filename']);
  169.             if ($asset) {
  170.                 $ewsNotification->setAttachment($asset);
  171.             }
  172.         }
  173.         if (!empty($params['x_img'])) {
  174.             $asset $this->createAsset($params['x_img'], time() . "EWSTwitterImages.png");
  175.             if ($asset) {
  176.                 $ewsNotification->setXAttachment($asset);
  177.             }
  178.         }
  179.         $ewsNotification->setXPost($x_post);
  180.         $ewsNotification->setEnableTwitterNotification($enableTwitterNotification);
  181.         $ewsNotification->setEnableSMSNotification($enableSMSNotification);
  182.         $ewsNotification->setEnableEmailNotification($enableEmailNotification);
  183.         $ewsNotification->setUser($userInterface);
  184.         $ewsNotification->setEditor($userInterface);
  185.         $ewsNotification->setPublished(false);
  186.         $ewsNotification->setPreviewText($previewText);
  187.         //$ewsNotification->save(["versionNote" => "Update"]);
  188.         //set ews search Id
  189.         $currentDate = new \DateTime();
  190.         $formattedDate $currentDate->format('dmY') . '-' $ewsNotification->getId();
  191.         $searchIdEn 'Early Warning System | ' $formattedDate ' | ' ucfirst($ewsNotification->getAlertType()?->getColor()) . ' Alert | ' $ewsNotification->getWeatherPhenomenon()?->getTitle("en");
  192.         $searchIdAr $translator->trans('Early Warning System', [], null"ar") . ' | ' $formattedDate ' | ' $translator->trans(ucfirst($ewsNotification->getAlertType()?->getColor()) . ' Alert', [], null"ar") . ' | ' $ewsNotification->getWeatherPhenomenon()?->getTitle("ar");
  193.         $ewsNotification->setEwsSearchId($searchIdEn"en");
  194.         $ewsNotification->setEwsSearchId($searchIdAr"ar");
  195.         //$ewsNotification->save(["versionNote" => "Update"]);
  196.         $ewsNotification->save();
  197.         return ['success' => true'message' => $translator->trans('ews_notification_added_successifully'), "notification_id" => $ewsNotification->getId()];
  198.         // } catch (\Exception $ex) {
  199.         //     $result = ['success' => false, 'message' => $ex->getMessage()];
  200.         // }
  201.         return $result;
  202.     }
  203.     public function updateNotification($request$params$userInterface$translator$userGroupIds$isPublished$emailService$templating$logger): array
  204.     {
  205.         $result = [];
  206.         // try {
  207.         $id $params['id'] ?? null;
  208.         if (!$id) {
  209.             throw new \Exception("EWS id is required");
  210.         }
  211.         $regionId $params['regionId'] ?? null;
  212.         $governorateIds $params['governateIds'] ?? null;
  213.         $municipalities $params['municipalityIds'] ?? null;
  214.         $ewsOtherLocationIds $params['ewsOtherLocationIds'] ?? null;
  215.         $alertActionId $params['alertActionId'] ?? null;
  216.         $alertTypeId $params['alertTypeId'] ?? null;
  217.         $weatherPhenomenonId $params['weatherPhenomenonId'] ?? null;
  218.         $coordinates json_encode($params['coordinates'] ?? []);
  219.         $message $params['message'] ?? [];
  220.         $alertHazardId $params['alertHazardId'] ?? null;
  221.         $enableTwitterNotification $params['enableTwitterNotification'] ?? false;
  222.         $enableSMSNotification $params['enableSMSNotification'] ?? false;
  223.         $enableEmailNotification $params['enableEmailNotification'] ?? false;
  224.         $previewText = isset($params['previewText']) ? $params['previewText'] : false;
  225.         $ewsNotification \Pimcore\Model\DataObject::getById($id);
  226.         if (!$ewsNotification instanceof \Pimcore\Model\DataObject\EwsNotification) {
  227.             // throw new \Exception("Ews notification not found");
  228.             return ['success' => false'message' => $translator->trans('ews_notification_not_found')];
  229.         }
  230.         if (isset($params['startDate'])) {
  231.             $startTime $params['startTime'] ?? '00:00:00';
  232.             $fromDate $params['startDate'] . ' ' $startTime;
  233.             $ewsNotification->setStartDate(\Carbon\Carbon::createFromFormat('Y-m-d H:i:s'$fromDate));
  234.         }
  235.         if (isset($params['endDate'])) {
  236.             $endTime $params['endTime'] ?? '00:00:00';
  237.             $endDate $params['endDate'] . ' ' $endTime;
  238.             $ewsNotification->setEndDate(\Carbon\Carbon::createFromFormat('Y-m-d H:i:s'$endDate));
  239.         }
  240.         if (isset($message['en'])) {
  241.             $ewsNotification->setMessage($message['en'], 'en');
  242.         }
  243.         if (isset($message['ar'])) {
  244.             $ewsNotification->setMessage($message['ar'], 'ar');
  245.         }
  246.         if (!empty($regionId)) {
  247.             $regionObj DataObject\Region::getByRegionId($regionId1);
  248.             if (!empty($regionObj)) {
  249.                 $ewsNotification->setRegion($regionObj);
  250.                 if ($governorateIds) {
  251.                     $governorateIdArr explode(","$governorateIds);
  252.                     if ($governorateIdArr) {
  253.                         $applyGovernates = [];
  254.                         foreach ($governorateIdArr as $id) {
  255.                             if(!empty($id)){
  256.                                 $applyGovernates[] = \Pimcore\Model\DataObject\Governorate::getByGovernoteId($idtrue);
  257.                                 if (!empty(array_unique($applyGovernates))) {
  258.                                     $ewsNotification->setGovernorate(array_unique($applyGovernates));
  259.                                 }
  260.                             }
  261.                         }
  262.                     }
  263.                     if ($municipalities) {
  264.                         $municipalitiesIdArr explode(","$municipalities);
  265.                         if ($municipalitiesIdArr) {
  266.                             $applyMunicipalities = [];
  267.                             foreach ($municipalitiesIdArr as $id) {
  268.                                 $applyMunicipalities[] = \Pimcore\Model\DataObject\Municipality::getByMunicipalityid($idtrue);
  269.                                 if (!empty(array_unique($applyMunicipalities))) {
  270.                                     $ewsNotification->setMunicipality(array_unique($applyMunicipalities));
  271.                                 }
  272.                             }
  273.                         }
  274.                     }
  275.                 } else {
  276.                     $ewsNotification->setGovernorate($this->governorateByRegion($regionObj->getId()));
  277.                 }
  278.             }
  279.         }
  280.         if ($ewsOtherLocationIds) {
  281.             $ewsOtherLocationIdArr explode(","$ewsOtherLocationIds);
  282.             if ($ewsOtherLocationIdArr) {
  283.                 $applyOtherGovernates = [];
  284.                 foreach ($ewsOtherLocationIdArr as $id) {
  285.                     $applyOtherGovernates[] = \Pimcore\Model\DataObject\EwsOtherLocation::getByGovernoteId($idtrue);
  286.                     if (!empty(array_unique($applyOtherGovernates))) {
  287.                         $ewsNotification->setEwsOtherLocations(array_unique($applyOtherGovernates));
  288.                     }
  289.                 }
  290.             }
  291.         }
  292.         if (!empty($alertTypeId)) {
  293.             $alertType DataObject\AlertType::getByAlertTypeId($alertTypeId1);
  294.             $ewsNotification->setAlertType($alertType);
  295.         }
  296.         if (!empty($alertActionId)) {
  297.             $alertActionArr explode(","$alertActionId);
  298.             if ($alertActionArr) {
  299.                 $alertActionArrRequest = [];
  300.                 foreach ($alertActionArr as $alertActionId) {
  301.                     $alertAction DataObject\AlertAction::getByAlertActionId($alertActionId1);
  302.                     if ($alertAction) {
  303.                         $alertActionArrRequest[] = $alertAction;
  304.                     }
  305.                 }
  306.                 $ewsNotification->setAlertAction($alertActionArrRequest);
  307.             }
  308.         }
  309.         if (!empty($weatherPhenomenonId)) {
  310.             $alertStatus DataObject\AlertStatus::getByAlertStatusId($weatherPhenomenonIdtrue);
  311.             $ewsNotification->setAlertStatus($alertStatus);
  312.         }
  313.         if (!empty($alertHazardId)) {
  314.             $alertHazardArr explode(","$alertHazardId);
  315.             if ($alertHazardArr) {
  316.                 $alertHazardArrRequest = [];
  317.                 foreach ($alertHazardArr as $alertHazardId) {
  318.                     $weatherPhenomenonAffect DataObject\AlertHazard::getByAlertHazardId($alertHazardId1);
  319.                     if ($weatherPhenomenonAffect) {
  320.                         $alertHazardArrRequest[] = $weatherPhenomenonAffect;
  321.                     }
  322.                 }
  323.                 $ewsNotification->setAlertHazard($alertHazardArrRequest);
  324.             }
  325.         }
  326.         $ewsNotification->setCoordinates($coordinates);
  327.         $addressComponents $params['address_components'] ?? [];
  328.         $this->addAddressComponentsFieldCollection($addressComponents$ewsNotification);
  329.         if (!empty($params['file'])) {
  330.             $asset $this->createAsset($params['file'], time() . $params['filename']);
  331.             if ($asset) {
  332.                 $ewsNotification->setAttachment($asset);
  333.             }
  334.         }
  335.         if (!empty($params['x_img'])) {
  336.             $asset $this->createAsset($params['x_img'], time() . "EWSTwitterImages.png");
  337.             if ($asset) {
  338.                 $ewsNotification->setXAttachment($asset);
  339.             }
  340.         }
  341.         if (!empty($params['x_post'])) {
  342.             $ewsNotification->setXPost($params['x_post']);
  343.         }
  344.         $ewsNotification->setEnableTwitterNotification($enableTwitterNotification);
  345.         $ewsNotification->setEnableSMSNotification($enableSMSNotification);
  346.         $ewsNotification->setEnableEmailNotification($enableEmailNotification);
  347.         $ewsNotification->setPreviewText($previewText);
  348.         //$ewsNotification->setPublished(true);
  349.         $ewsNotification->setUser($userInterface);
  350.         $ewsNotification->setEditor($userInterface);
  351.         //set ews search Id
  352.         $currentDate = new \DateTime();
  353.         $formattedDate $currentDate->format('dmY') . '-' $ewsNotification->getId();
  354.         $searchIdEn 'Early Warning System | ' $formattedDate ' | ' ucfirst($ewsNotification->getAlertType()?->getColor()) . ' Alert | ' $ewsNotification->getWeatherPhenomenon()?->getTitle("en");
  355.         $searchIdAr $translator->trans('Early Warning System', [], null"ar") . ' | ' $formattedDate ' | ' $translator->trans(ucfirst($ewsNotification->getAlertType()?->getColor()) . ' Alert', [], null"ar") . ' | ' $ewsNotification->getWeatherPhenomenon()?->getTitle("ar");
  356.         $ewsNotification->setEwsSearchId($searchIdEn"en");
  357.         $ewsNotification->setEwsSearchId($searchIdAr"ar");
  358.         $ewsNotification->save(["versionNote" => "Update"]);
  359.         if ($isPublished) {
  360.             // send email
  361.             // Ensure you use 'php' to execute the command.
  362.             $jsonUserGroupIds json_encode($userGroupIds);
  363.             $process = new Process(['php''bin/console''app:send-early-warning-alert-email''--alertId=' $ewsNotification->getId(), '--userGroupIds=' $jsonUserGroupIds]);
  364.             $process->setWorkingDirectory(PIMCORE_PROJECT_ROOT);
  365.             try {
  366.                 $process->mustRun();
  367.                 $result['success'] = true;
  368.                 $logger->info("update EwsNotification command executed successfully: " $process->getOutput());
  369.                 $result['message'] = $process->getOutput();
  370.             } catch (ProcessFailedException $exception) {
  371.                 $logger->error("update EwsNotification command failed: " $exception->getMessage());
  372.                 return ['success' => false'message' => $exception->getMessage()];
  373.             }
  374.         }
  375.         return ['success' => true'message' => $translator->trans('ews_notification_updated_successifully'), "notification_id" => $ewsNotification->getId()];
  376.         // } catch (\Exception $ex) {
  377.         //     $result = ['success' => false, 'message' => $ex->getMessage()];
  378.         // }
  379.         return $result;
  380.     }
  381.     public function governorateByRegion($regionId)
  382.     {
  383.         $governorateArr = [];
  384.         $governorateList = new DataObject\Governorate\Listing();
  385.         $governorateList->setCondition('regionId__id = ?'$regionId);
  386.         $governorateList->load();
  387.         if (!empty($governorateList)) {
  388.             foreach ($governorateList as $governorate) {
  389.                 array_push($governorateArr$governorate);
  390.             }
  391.         }
  392.         return $governorateArr;
  393.     }
  394.     public function notificationListing($params$paginator$translator): array
  395.     {
  396.         $response = [];
  397.         // try {
  398.         $pageSize = isset($params['page_size']) ? $params['page_size'] : LIMIT_PER_PAGE;
  399.         $page = isset($params['page']) ? $params['page'] : 1;
  400.         $notificationList = new DataObject\EwsNotification\Listing();
  401.         $notificationList->setUnpublished(true);
  402.         if ($params['unpublished'] == false) {
  403.             $notificationList->filterByPublished(true);
  404.         } else {
  405.             $notificationList->filterByPublished(false);
  406.         }
  407.         if (isset($params['region']) && !empty($params['region'])) {
  408.             $regionSql null;
  409.             $alertRegion \Pimcore\Model\DataObject\Region::getByRegionId($params['region'], true);
  410.             if ($alertRegion) {
  411.                 $regionSql .= "region__id = " $alertRegion->getId() . " OR ";
  412.             }
  413.             $notificationList->addConditionParam("(" substr($regionSql0, -3) . ")");
  414.         }
  415.         if (isset($params['status']) && !empty($params['status'])) {
  416.             $notificationList->addConditionParam("status IN (?)", [$params['status']]);
  417.         }
  418.         if (isset($params['addressCmp']) && !empty($params['addressCmp'])) {
  419.             //TODO
  420.         }
  421.         if (isset($params['start_date']) && !empty($params['start_date'])  && isset($params['end_date']) && !empty($params['end_date'])) {
  422.             $notificationList->filterByStartDate(strtotime($params['start_date']), ">=");
  423.             $notificationList->filterByEndDate(strtotime($params['end_date']), "<=");
  424.         }
  425.         if (isset($params['fromDate']) && isset($params['toDate']) && !empty($params['fromDate']) && !empty($params['toDate'])) {
  426.             $fromDate = new \DateTime($params['fromDate']);
  427.             $toDate = new \DateTime($params['toDate']);
  428.             $fromDateStr strtotime($fromDate->format('Y-m-d H:i:s'));
  429.             $toDateStr strtotime($toDate->format('Y-m-d') . ' 23:59:59');
  430.             $notificationList->addConditionParam(
  431.                 "(o_creationDate >= ? AND o_creationDate <= ?)",
  432.                 [$fromDateStr$toDateStr]
  433.             );
  434.         }
  435.         // Set default sorting
  436.         $notificationList->setOrderKey("o_creationDate");
  437.         $notificationList->setOrder("desc");
  438.         // Load all data for proper sorting
  439.         $notificationList->load();
  440.         if ($notificationList) {
  441.             // Create structured data for sorting (same approach as searchEwsNotification)
  442.             $sortedData = [];
  443.             foreach ($notificationList as $notification) {
  444.                 $regionEn $notification->getRegion() ? $notification->getRegion()->getName("en") : '';
  445.                 $startDate $notification->getStartDate() ? $notification->getStartDate()->format('Y-m-d H:i:s') : '';
  446.                 $endDate $notification->getEndDate() ? $notification->getEndDate()->format('Y-m-d H:i:s') : '';
  447.                 $alertStatusEn = !empty($notification->getAlertStatus()) ? $notification->getAlertStatus()->getName("en") : "";
  448.                 $creationDate $notification->getCreationDate();
  449.                 $title $notification->getStartDate() ? $notification->getStartDate()->format("dmY") . '-' $notification->getId() : '';
  450.                 $alertTypeEn $notification->getAlertType() ? ucwords($notification->getAlertType()->getColor()) : '';
  451.                 $fromDate $notification->getStartDate() ? $notification->getStartDate()->format('Y-m-d H:i:s') : '';
  452.                 $toDate $notification->getEndDate() ? $notification->getEndDate()->format('Y-m-d H:i:s') : '';
  453.                 $created_at $notification->getCreationDate() ? date("Y-m-d H:i:s"$notification->getCreationDate()) : '';
  454.                 
  455.                 $sortedData[] = [
  456.                     'notification' => $notification,
  457.                     'region' => $regionEn,
  458.                     'alertStatus' => $alertStatusEn,
  459.                     'startDate' => $startDate,
  460.                     'endDate' => $endDate,
  461.                     'creationDate' => $creationDate,
  462.                     'title' => $title,
  463.                     'alertType' => $alertTypeEn,
  464.                     'fromDate' => $fromDate,
  465.                     'toDate' => $toDate,
  466.                     'created_at' => $created_at
  467.                 ];
  468.             }
  469.             
  470.             // Apply sorting to the complete dataset
  471.             // Support multiple parameter names for sorting
  472.             $orderKey $params['orderkey'] ?? $params['orderKey'] ?? $params['sortBy'] ?? null;
  473.             $orderDirection = isset($params['order']) ? strtolower($params['order']) : 'desc';
  474.             
  475.             if ($orderKey && in_array($orderKey, ['title''alertType''fromDate''toDate''region''alertStatus''created_at'])) {
  476.                 usort($sortedData, function($a$b) use ($orderKey$orderDirection) {
  477.                     $valueA $a[$orderKey] ?? '';
  478.                     $valueB $b[$orderKey] ?? '';
  479.                     
  480.                     // Handle null/empty values
  481.                     if (empty($valueA) && !empty($valueB)) {
  482.                         return $orderDirection === 'asc' ? -1;
  483.                     }
  484.                     if (!empty($valueA) && empty($valueB)) {
  485.                         return $orderDirection === 'asc' : -1;
  486.                     }
  487.                     if (empty($valueA) && empty($valueB)) {
  488.                         return 0;
  489.                     }
  490.                     
  491.                     // For date fields, convert to timestamp for proper comparison
  492.                     if (in_array($orderKey, ['fromDate''toDate''created_at'])) {
  493.                         $timestampA strtotime($valueA);
  494.                         $timestampB strtotime($valueB);
  495.                         
  496.                         if ($timestampA === false || $timestampB === false) {
  497.                             // If date parsing fails, fall back to string comparison
  498.                             $comparison strcasecmp($valueA$valueB);
  499.                         } else {
  500.                             $comparison $timestampA <=> $timestampB;
  501.                         }
  502.                     } else {
  503.                         // For text fields, use case-insensitive string comparison
  504.                         $comparison strcasecmp($valueA$valueB);
  505.                     }
  506.                     
  507.                     return $orderDirection === 'asc' $comparison : -$comparison;
  508.                 });
  509.             }
  510.             
  511.             // Extract the sorted notifications
  512.             $sortedNotifications array_map(function ($item) {
  513.                 return $item['notification'];
  514.             }, $sortedData);
  515.             
  516.             // Convert to response format
  517.             foreach ($sortedNotifications as $notification) {
  518.                 $response[] = $this->createNotificationFormat($notification$translator);
  519.             }
  520.         }
  521.         
  522.         // If no paginator, return all sorted data
  523.         if ($paginator == null) {
  524.             return ["success" => TRUE"data" => $response];
  525.         }
  526.         $paginator $paginator->paginate(
  527.             $response,
  528.             $page,
  529.             $pageSize
  530.         );
  531.         return ["success" => TRUE"data" => $paginator"paginationVariables" => $paginator->getPaginationData()];
  532.         // return $response;
  533.         // } catch (\Exception $ex) {
  534.         //     $result = ["success" => false, "message" => $ex->getMessage()];
  535.         // }
  536.         // return $result;
  537.     }
  538.     public function capAlertNotificationListingxml($request$params$lang$translator)
  539.     {
  540.         
  541.         try {
  542.             $notificationList = new DataObject\EwsNotification\Listing();
  543.           
  544.             
  545.             // Get the timezone for date operations
  546.             $timezone = new \DateTimeZone(TIMEZONE);
  547.             
  548.             // Filter by pubDate if provided (format: d-m-Y, e.g., 25-12-2025)
  549.             if (isset($params['pubDate']) && !empty($params['pubDate'])) {
  550.                 $pubDateStr $params['pubDate'];
  551.                 // Parse the date from d-m-Y format
  552.                 $pubDate \DateTime::createFromFormat('d-m-Y'$pubDateStr$timezone);
  553.                 if ($pubDate === false) {
  554.                     // Try alternative format Y-m-d
  555.                     $pubDate \DateTime::createFromFormat('Y-m-d'$pubDateStr$timezone);
  556.                 }
  557.                 
  558.                 if ($pubDate !== false) {
  559.                     // Set time to start of day (00:00:00)
  560.                     $pubDate->setTime(000);
  561.                     $startTimestamp $pubDate->getTimestamp();
  562.                     
  563.                     // Set time to end of day (23:59:59)
  564.                     $pubDateEnd = clone $pubDate;
  565.                     $pubDateEnd->setTime(235959);
  566.                     $endTimestamp $pubDateEnd->getTimestamp();
  567.                     
  568.                     // Filter notifications where startDate falls within the specified date
  569.                     // Use filterByStartDate for >= start of day
  570.                     $notificationList->filterByStartDate($startTimestamp">=");
  571.                     // Add condition for <= end of day using addConditionParam
  572.                     $notificationList->addConditionParam("startDate <= ?", [$endTimestamp]);
  573.                 }
  574.             }else{
  575.                 $notificationList->setCondition("endDate > ?", [time()]);
  576.             }
  577.             
  578.             $notificationList->setOrderKey("oo_id");
  579.             $notificationList->setOrder("desc");
  580.           
  581.             // Get the current date as a DateTime object
  582.             $currentDate = new \DateTime('now'$timezone);
  583.             // Set the condition to filter for notifications where endDate is greater than the current date
  584.             
  585.             // Loop through the notifications to find the last updated date
  586.             // Initialize $lastUpdatedDate with current date as default
  587.             $lastUpdatedDate $currentDate;
  588.             foreach ($notificationList as $notification) {
  589.                 $notificationUpdatedDate $notification->getEndDate()->setTimezone($timezone);
  590.                 if ($notificationUpdatedDate $lastUpdatedDate) {
  591.                     $lastUpdatedDate $notificationUpdatedDate;
  592.                 }
  593.             }
  594.             // Create an empty RSS XML structure
  595.             // $rss = new SimpleXMLElement('<rss version="2.0"/>');
  596.             $rss = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"/>');
  597.             // Create the <channel> element
  598.             $channel $rss->addChild('channel');
  599.             // Add elements inside <channel>
  600.             $channel->addChild('title'$translator->trans("Early Warning System from National Center of Meteorology, Saudi Arabia"));
  601.             $channel->addChild('link'$_ENV['BASE_URL_DEMO_AJWAA_API'] . '/' $lang '/cap-alerts');
  602.             $channel->addChild('description'$translator->trans("Early Warning System from National Center of Meteorology, Saudi Arabia"));
  603.             $channel->addChild('language'$lang);
  604.             $channel->addChild('copyright'$translator->trans("Copyright (c) ".date('Y').", NCM. Licensed under Creative Commons BY 4.0"));
  605.             $channel->addChild('pubDate'$lastUpdatedDate->format('D, d M Y H:i:s \G\M\T'));
  606.             // Create the <image> element
  607.             $image $channel->addChild('image');
  608.             $image->addChild('title'$translator->trans("Early Warning System from National Center of Meteorology, Saudi Arabia"));
  609.             $image->addChild('url'$_ENV['BASE_URL_DEMO_AJWAA_API'] . '/assets/images/logo2.3.png');
  610.             $image->addChild('link'$_ENV['BASE_URL_DEMO_AJWAA_API'] . '/' $lang '/cap-alerts');
  611.             // Create multiple <alert> elements and add them to the <alerts> element
  612.             // Create child elements and add them to the alert
  613.             foreach ($notificationList as $notification) { // Create two <alert> elements as an example
  614.                 $hazardNames "";
  615.                 if (!empty($notification->getAlertHazard())) {
  616.                     $hazardNamesArr = [];
  617.                     foreach ($notification->getAlertHazard() as $hazard) {
  618.                         $hazardNamesArr[] = $hazard->getName($lang);
  619.                     }
  620.                     $hazardNames .= implode(", "$hazardNamesArr);
  621.                 }
  622.                 $governorateNames "";
  623.                 $governorateCoordinates "";
  624.                 $governorateNamesArr = [];
  625.                 if ($notification->getGovernorate() && !empty($notification->getGovernorate())) {
  626.                     $governorateCoordinatesArr = [];
  627.                     foreach ($notification->getGovernorate() as $governorate) {
  628.                         $governorateNamesArr[] = $governorate->getName($lang);
  629.                         $governorateCoordinatesArr[] = $governorate->getLatitude() . "," $governorate->getLongitude();
  630.                     }
  631.                     $governorateCoordinates .= implode(" "$governorateCoordinatesArr);
  632.                     
  633.                     // Format governorates with "and" before the last one
  634.                     if (count($governorateNamesArr) > 1) {
  635.                         $lastGovernorate array_pop($governorateNamesArr);
  636.                         $governorateNames implode(" - "$governorateNamesArr) . " - " $lastGovernorate;
  637.                     } else {
  638.                         $governorateNames $governorateNamesArr[0];
  639.                     }
  640.                 }
  641.                 // Get region and alert status names
  642.                 $regionName $notification->getRegion() ? $notification->getRegion()->getName($lang) : $translator->trans("UNKNOWN");
  643.                 $alertStatusName = !empty($notification->getAlertStatus()) ? $notification->getAlertStatus()->getName($lang) : $translator->trans("UNKNOWN");
  644.                 // Format title
  645.                 $title =  $alertStatusName ", " $regionName;
  646.                 // Format dates in GMT format for description
  647.                 $startDateGMT = !empty($notification->getStartDate()) ? $notification->getStartDate()->setTimezone(new \DateTimeZone('GMT'))->format('D, d M Y H:i:s \G\M\T') : $translator->trans("UNKNOWN");
  648.                 $endDateGMT = !empty($notification->getEndDate()) ? $notification->getEndDate()->setTimezone(new \DateTimeZone('GMT'))->format('D, d M Y H:i:s \G\M\T') : $translator->trans("UNKNOWN");
  649.                 
  650.                 // Format description: "{Hazard names} From: {startDate GMT} To: {endDate GMT}, {Location}"
  651.                 $descriptionParts = [];
  652.                 // if (!empty($hazardNames)) {
  653.                 //     $descriptionParts[] = $hazardNames;
  654.                 // }
  655.                 $descriptionParts[] = $alertStatusName " " $translator->trans("From") . ": " $startDateGMT " " $translator->trans("To") . ": " $endDateGMT;
  656.                 if (!empty($governorateNames)) {
  657.                     $descriptionParts[] = ", " $governorateNames;
  658.                 }
  659.                 $description implode(" "$descriptionParts);
  660.                 // Create one or more <item> elements
  661.                 $item $channel->addChild('item');
  662.               
  663.                 $item->addChild('guid'"{" $notification->getGuid() . "}");
  664.                 $item->addChild('title'$title);
  665.                 $item->addChild('link'$_ENV['BASE_URL_DEMO_AJWAA_API'] . '/cap/' $lang '/alerts/' $notification->getGuid() . '.xml');
  666.              
  667.                 $item->addChild('description',$description);
  668.                 $item->addChild('author'AUTHOR);
  669.                 $item->addChild('pubDate'$notification->getLastModifiedDate()->setTimeZone(new \DateTimeZone('GMT'))->format('D, d M Y H:i:s \G\M\T'));
  670.             }
  671.             // Create additional <item> elements as needed
  672.             // Format the XML for output
  673.             $dom dom_import_simplexml($rss)->ownerDocument;
  674.             $dom->formatOutput true;
  675.             // Save the XML to a file
  676.             $xmlString $dom->saveXML();
  677.             // Set the Content-Type header to specify that the response is XML
  678.             header("Content-Type: application/xml");
  679.             // Save the XML to a file or output it
  680.             $xmlString $rss->saveXML();
  681.             print $xmlString;
  682.             exit;
  683.             return $xmlString;
  684.         } catch (\Exception $ex) {
  685.             $result = ["success" => false"message" => $ex->getMessage()];
  686.         }
  687.         return $result;
  688.     }
  689.     public function capAlertNotificationDetailxml($guid$lang$translator)
  690.     {
  691.         $response = [];
  692.         $timezone = new \DateTimeZone(TIMEZONE);
  693.         try {
  694.             $notification \Pimcore\Model\DataObject\EwsNotification::getByGuid($guidtrue);
  695.             if (empty($notification)) {
  696.                 throw new \Exception('Notification not found.');
  697.             }
  698.             $doc = new DomDocument('1.0''UTF-8');
  699.             // Create the root element
  700.             $alert $doc->createElement('alert');
  701.             $alert->setAttribute('xmlns''urn:oasis:names:tc:emergency:cap:1.2');
  702.             $alert->setAttribute('xmlns:xsl''http://www.w3.org/1999/XSL/Transform');
  703.             $alert->setAttribute('xmlns:ds''http://www.w3.org/2000/09/xmldsig#');
  704.             $doc->appendChild($alert);
  705.             // Create child elements and add them to the alert
  706.             $identifier $doc->createElement('identifier',  $notification->getGuid()); //uuid
  707.             $sender $doc->createElement('sender'$translator->trans('NCM'));
  708.             $sent $doc->createElement('sent'$notification->getStartDate()->setTimezone($timezone)->format('Y-m-d\TH:i:sP'));
  709.             $status $doc->createElement('status''Actual');
  710.             $msgType $doc->createElement('msgType''Alert');
  711.             $scope $doc->createElement('scope''Public');
  712.             $alert->appendChild($identifier);
  713.             $alert->appendChild($sender);
  714.             $alert->appendChild($sent);
  715.             $alert->appendChild($status);
  716.             $alert->appendChild($msgType);
  717.             $alert->appendChild($scope);
  718.             // Create the <info> element and add it to the alert
  719.             $info $doc->createElement('info');
  720.             $alert->appendChild($info);
  721.             // Create child elements for the <info> element
  722.             $category $doc->createElement('category','Met');
  723.             $event $doc->createElement('event'$notification->getAlertStatus() ? $notification->getAlertStatus()->getName($lang) : $translator->trans('UNKNOWN')); //high rain
  724.             $responseType $doc->createElement('responseType''None');
  725.             $effective $doc->createElement('effective'$notification->getStartDate()->setTimezone($timezone)->format('Y-m-d\TH:i:sP'));
  726.             
  727.             // Determine CAP urgency, severity, and certainty based on alert type and event
  728.             $eventName $notification->getAlertStatus() ? strtolower($notification->getAlertStatus()->getName('en')) : '';
  729.             $alertTypeColor $notification->getAlertType() ? strtoupper($notification->getAlertType()->getColor() ?? '') : '';
  730.             
  731.             // Determine urgency based on alert type and event severity
  732.             // Immediate: Life-threatening, requires immediate action (RED alerts, tornadoes, etc.)
  733.             // Expected: Forecasted events that will occur (most weather alerts)
  734.             // Past: Events that have already occurred
  735.             // Future: Long-term forecasts
  736.             $urgency 'Expected'// Default for most weather alerts
  737.             if ($alertTypeColor === 'RED' || stripos($eventName'tornado') !== false || stripos($eventName'extreme') !== false) {
  738.                 $urgency 'Immediate';
  739.             }
  740.             
  741.             // Determine severity based on alert type color and event
  742.             // Extreme: Widespread destruction
  743.             // Severe: Significant threat to life/property
  744.             // Moderate: Some threat
  745.             // Minor: Minimal threat
  746.             // Unknown: Severity unknown
  747.             $severity 'Moderate'// Default
  748.             if ($alertTypeColor === 'RED') {
  749.                 $severity 'Extreme';
  750.             } elseif ($alertTypeColor === 'ORANGE') {
  751.                 $severity 'Severe';
  752.             } elseif ($alertTypeColor === 'YELLOW') {
  753.                 $severity 'Moderate';
  754.             } elseif ($alertTypeColor === 'GREEN' || stripos($eventName'light') !== false || stripos($eventName'minor') !== false) {
  755.                 $severity 'Minor';
  756.             }
  757.             
  758.             // Certainty: Observed (happening now), Likely (will occur), Possible (may occur), Unlikely, Unknown
  759.             $certainty 'Possible'// Default - assume observed for active alerts
  760.             
  761.             $urgencyElement $doc->createElement('urgency'$urgency);
  762.             $severityElement $doc->createElement('severity'$severity);
  763.             $certaintyElement $doc->createElement('certainty'$certainty);
  764.             
  765.             // Remove eventCode element - SAME codes are US-specific and not applicable
  766.             // If eventCode is needed in future, use WMO codes instead:
  767.             // $eventCode = $doc->createElement('eventCode');
  768.             // $eventCodeValueName = $doc->createElement('valueName', 'WMO');
  769.             // $eventCodeValueText = $doc->createElement('value', 'FOG'); // Example WMO code
  770.             // $eventCode->appendChild($eventCodeValueName);
  771.             // $eventCode->appendChild($eventCodeValueText);
  772.             
  773.             // Fix: onset should use startDate (when the event begins), not endDate
  774.             $onsetDate $notification->getStartDate() ? $notification->getStartDate()->setTimezone($timezone) : null;
  775.             if (!$onsetDate) {
  776.                 // Fallback to current time if startDate is not available
  777.                 $onsetDate = new \DateTime('now'$timezone);
  778.             }
  779.             $onset $doc->createElement('onset'$onsetDate->format('Y-m-d\TH:i:sP'));
  780.             
  781.             // Fix: expires must be after onset (use endDate, ensure it's after startDate)
  782.             $expiresDate $notification->getEndDate() ? $notification->getEndDate()->setTimezone($timezone) : null;
  783.             if (!$expiresDate) {
  784.                 // Fallback: if no endDate, set expires to 24 hours after onset
  785.                 $expiresDate = clone $onsetDate;
  786.                 $expiresDate->modify('+24 hours');
  787.             } else {
  788.                 // Ensure expires is after onset (CAP requirement)
  789.                 if ($expiresDate <= $onsetDate) {
  790.                     // If expires equals or is before onset, add at least 1 hour
  791.                     $expiresDate = clone $onsetDate;
  792.                     $expiresDate->modify('+1 hour');
  793.                 }
  794.             }
  795.             $expires $doc->createElement('expires'$expiresDate->format('Y-m-d\TH:i:sP'));
  796.             $senderName $doc->createElement('senderName'$translator->trans('NCM'));
  797.              $headline $doc->createElement('headline'$notification->getAlertType()->getName($lang) . ', ' .  $notification->getAlertStatus()->getName($lang));
  798.             // $headline = $doc->createElement(
  799.             //     'headline',
  800.             //     $translator->trans('Early Warning Alert in') . ' ' .
  801.             //     (
  802.             //         $notification->getRegion()
  803.             //             ? $notification->getRegion()->getName($lang)
  804.             //             : $translator->trans('UNKNOWN')
  805.             //     ) . ': ' .
  806.             //     (
  807.             //         $notification->getAlertStatus()
  808.             //             ? $notification->getAlertStatus()->getName($lang)
  809.             //             : $translator->trans('UNKNOWN')
  810.             //     )
  811.             // );
  812.             $hazardNames "";
  813.             if (!empty($notification->getAlertHazard())) {
  814.                 $hazardNamesArr = [];
  815.                 foreach ($notification->getAlertHazard() as $hazard) {
  816.                     $hazardNamesArr[] = $hazard->getName($lang);
  817.                 }
  818.                 $hazardNames .= implode(", "$hazardNamesArr);
  819.             }
  820.             $governorateNames "";
  821.             $governorateCoordinates "";
  822.             $governorateNamesArr = [];
  823.             if ($notification->getGovernorate() && !empty($notification->getGovernorate())) {
  824.                 $governorateCoordinatesArr = [];
  825.                 foreach ($notification->getGovernorate() as $governorate) {
  826.                     $governorateNamesArr[] = $governorate->getName($lang);
  827.                     $governorateCoordinatesArr[] = $governorate->getLatitude() . "," $governorate->getLongitude();
  828.                 }
  829.                 // Format governorates with "و" for Arabic or "and" for English before the last one
  830.                 if (count($governorateNamesArr) > 1) {
  831.                     $lastGovernorate array_pop($governorateNamesArr);
  832.                     $andText = ($lang == 'ar') ? ' و ' ', and ';
  833.                     $governorateNames implode(", "$governorateNamesArr) . $andText $lastGovernorate;
  834.                 } else {
  835.                     $governorateNames $governorateNamesArr[0];
  836.                 }
  837.                 $governorateCoordinates .= implode(" "$governorateCoordinatesArr);
  838.             }
  839.             // Get region and alert status names
  840.             $regionName $notification->getRegion() ? $notification->getRegion()->getName($lang) : $translator->trans("UNKNOWN");
  841.             $alertStatusName $notification->getAlertStatus() ? $notification->getAlertStatus()->getName($lang) : $translator->trans("UNKNOWN");
  842.             
  843.             // Format dates
  844.             $startDateFormatted = !empty($notification->getStartDate()) ? $notification->getStartDate()->setTimezone($timezone)->format('d/m/Y H:i:s A') : $translator->trans("UNKNOWN");
  845.             $endDateFormatted = !empty($notification->getEndDate()) ? $notification->getEndDate()->setTimezone($timezone)->format("d/m/Y H:i:s A") : $translator->trans("UNKNOWN");
  846.             
  847.             // Format description: Comma-separated list of hazard names (e.g., "Raised dust, Active winds,Near lack of horizontal visibility (1-3) km")
  848.             $descriptionText = !empty($hazardNames) ? $hazardNames "";
  849.             
  850.             $description $doc->createElement('description'$notification->getAlertStatus()->getName($lang) . ' ' $descriptionText);
  851.             
  852.             // Create actionable instruction based on alert type and hazards (CAP compliance)
  853.             // Handle multiple hazards by checking all types and combining instructions
  854.             $instructionText '';
  855.             if (!empty($hazardNames)) {
  856.                 $instructions = [];
  857.                 $hazardNamesLower strtolower($hazardNames);
  858.                 
  859.                 // Check for all hazard types and collect relevant instructions
  860.                 // Priority hazards first
  861.                 if (stripos($hazardNames'tornado') !== false || stripos($hazardNames'إعصار') !== false) {
  862.                     $instructions[] = ($lang == 'ar')
  863.                         ? 'انتقل فوراً إلى أقرب ملجأ أو الطابق السفلي. تجنب النوافذ والمناطق المكشوفة.'
  864.                         'Move immediately to the nearest shelter or basement. Avoid windows and exposed areas.';
  865.                 }
  866.                 
  867.                 if (stripos($hazardNames'rain') !== false || stripos($hazardNames'مطر') !== false || 
  868.                     stripos($hazardNames'flood') !== false || stripos($hazardNames'فيضان') !== false ||
  869.                     stripos($hazardNames'pooling') !== false || stripos($hazardNames'تجمع') !== false) {
  870.                     $instructions[] = ($lang == 'ar'
  871.                         ? 'انتقل إلى أرض مرتفعة لتجنب الفيضانات. تجنب القيادة في المناطق المغمورة.'
  872.                         'Move to higher ground to avoid flooding. Avoid driving through flooded areas.';
  873.                 }
  874.                 
  875.                 if (stripos($hazardNames'wave') !== false || stripos($hazardNames'موجة') !== false) {
  876.                     $instructions[] = ($lang == 'ar')
  877.                         ? 'تجنب المناطق الساحلية والأنشطة البحرية. ابق بعيداً عن الشواطئ.'
  878.                         'Avoid coastal areas and marine activities. Stay away from beaches.';
  879.                 }
  880.                 
  881.                 if (stripos($hazardNames'wind') !== false || stripos($hazardNames'رياح') !== false) {
  882.                     $instructions[] = ($lang == 'ar')
  883.                         ? 'تجنب الخروج من المنزل وتأكد من إغلاق النوافذ والأبواب بإحكام. تجنب القيادة إذا أمكن.'
  884.                         'Avoid going outside and ensure windows and doors are securely closed. Avoid driving if possible.';
  885.                 }
  886.                 
  887.                 if (stripos($hazardNames'thunder') !== false || stripos($hazardNames'رعد') !== false) {
  888.                     $instructions[] = ($lang == 'ar')
  889.                         ? 'تجنب المناطق المكشوفة والبقاء في الداخل. تجنب استخدام الأجهزة الإلكترونية والمياه.'
  890.                         'Avoid exposed areas and stay indoors. Avoid using electronic devices and water.';
  891.                 }
  892.                 
  893.                 if (stripos($hazardNames'hail') !== false || stripos($hazardNames'برد') !== false) {
  894.                     $instructions[] = ($lang == 'ar')
  895.                         ? 'ابق في الداخل وتجنب النوافذ. إذا كنت في الخارج، ابحث عن مأوى فوري.'
  896.                         'Stay indoors and avoid windows. If outside, seek immediate shelter.';
  897.                 }
  898.                 
  899.                 if (stripos($hazardNames'visibility') !== false || stripos($hazardNames'رؤية') !== false ||
  900.                     stripos($hazardNames'fog') !== false || stripos($hazardNames'ضباب') !== false ||
  901.                     stripos($hazardNames'low visibility') !== false || stripos($hazardNames'lack of horizontal visibility') !== false) {
  902.                     $instructions[] = ($lang == 'ar')
  903.                         ? 'تجنب القيادة إلا للضرورة القصوى. إذا كنت تقود، استخدم الأضواء المنخفضة واتبع المسافة الآمنة.'
  904.                         'Avoid driving except in emergencies. If driving, use low beams and maintain safe distance.';
  905.                 }
  906.                 
  907.                 if (stripos($hazardNames'snow') !== false || stripos($hazardNames'ثلج') !== false) {
  908.                     $instructions[] = ($lang == 'ar')
  909.                         ? 'تجنب السفر غير الضروري. إذا كنت في الخارج، ارتدِ ملابس دافئة وتجنب الطرق الزلقة.'
  910.                         'Avoid unnecessary travel. If outside, wear warm clothing and avoid slippery roads.';
  911.                 }
  912.                 
  913.                 if (stripos($hazardNames'frost') !== false || stripos($hazardNames'صقيع') !== false ||
  914.                     (stripos($hazardNames'temperature') !== false && stripos($hazardNames'drop') !== false) ||
  915.                     (stripos($hazardNames'درجة') !== false && stripos($hazardNames'انخفاض') !== false) ||
  916.                     stripos($hazardNames'below zero') !== false) {
  917.                     $instructions[] = ($lang == 'ar')
  918.                         ? 'احمِ نفسك من البرد الشديد. ارتدِ ملابس دافئة وتجنب التعرض الطويل للطقس البارد.'
  919.                         'Protect yourself from extreme cold. Wear warm clothing and avoid prolonged exposure to cold weather.';
  920.                 }
  921.                 
  922.                 if ((stripos($hazardNames'rise') !== false && stripos($hazardNames'temperature') !== false) ||
  923.                     (stripos($hazardNames'ارتفاع') !== false && stripos($hazardNames'درجة') !== false) ||
  924.                     stripos($hazardNames'degrees Celsius') !== false || stripos($hazardNames'درجة مئوية') !== false) {
  925.                     $instructions[] = ($lang == 'ar')
  926.                         ? 'احمِ نفسك من الحرارة الشديدة. ابق في أماكن مكيفة، اشرب الكثير من الماء، وتجنب الأنشطة الخارجية.'
  927.                         'Protect yourself from extreme heat. Stay in air-conditioned areas, drink plenty of water, and avoid outdoor activities.';
  928.                 }
  929.                 
  930.                 // Combine all instructions or use default
  931.                 if (!empty($instructions)) {
  932.                     // Remove duplicates and combine with appropriate separator
  933.                     $uniqueInstructions array_unique($instructions);
  934.                     $separator = ($lang == 'ar') ? ' ' ' ';
  935.                     $instructionText implode($separator$uniqueInstructions);
  936.                 } else {
  937.                     $instructionText = ($lang == 'ar')
  938.                         ? 'اتخذ الاحتياطات اللازمة واتبع التعليمات الرسمية. ابق في مكان آمن.'
  939.                         'Take necessary precautions and follow official instructions. Stay in a safe place.';
  940.                 }
  941.             } else {
  942.                 // Default instruction if no specific hazards
  943.                 $instructionText = ($lang == 'ar')
  944.                     ? 'اتخذ الاحتياطات اللازمة واتبع التعليمات الرسمية من المركز الوطني للأرصاد.'
  945.                     'Take necessary precautions and follow official instructions from the National Center of Meteorology.';
  946.             }
  947.             $instruction $doc->createElement('instruction'$instructionText);
  948.             
  949.             $contact $doc->createElement('contact'CONTACT);
  950.             
  951.             // Create dynamic web URL using notification ID (CAP compliance - absolute HTTPS URL)
  952.             $web $doc->createElement('web'htmlspecialchars($_ENV['NCM_PUBLIC_PORTAL_URL'] . $lang '/early-warning/'.$notification->getAlertId(), ENT_XML1'UTF-8'));
  953.             
  954.             $info->appendChild($category);
  955.             $info->appendChild($event);     
  956.             $info->appendChild($responseType);
  957.             $info->appendChild($urgencyElement);
  958.             $info->appendChild($severityElement);
  959.             $info->appendChild($certaintyElement);
  960.             // eventCode removed - SAME codes are US-specific
  961.             $info->appendChild($effective);
  962.             $info->appendChild($onset);
  963.             $info->appendChild($expires);
  964.             $info->appendChild($senderName);
  965.             $info->appendChild($headline);
  966.             $info->appendChild($description);
  967.             $info->appendChild($instruction);
  968.             $info->appendChild($web);
  969.             $info->appendChild($contact);
  970.             
  971.             
  972.             // Get polygon coordinates from JSON lookup sheet - returns array of polygons per governorate
  973.             $governoratePolygons = [];
  974.             
  975.             // Try to get polygons from the JSON lookup sheet first
  976.             if ($notification->getGovernorate() && !empty($notification->getGovernorate())) {
  977.                 // Convert collection to array if needed
  978.                 $governorates $notification->getGovernorate();
  979.                 if (is_object($governorates) && method_exists($governorates'getItems')) {
  980.                     $governorates $governorates->getItems();
  981.                 } elseif (!is_array($governorates)) {
  982.                     $governorates iterator_to_array($governorates);
  983.                 }
  984.                 if (!empty($governorates)) {
  985.                     $governoratePolygons $this->getPolygonFromJsonSheet($governorates);
  986.                 }
  987.             }
  988.           
  989.             // Fallback to notification coordinates if JSON lookup didn't provide polygons
  990.             if (empty($governoratePolygons)) {
  991.                 $coordinatesString $notification->getCoordinates();
  992.                
  993.                 if (!empty($coordinatesString)) {
  994.                     // Try to parse as JSON (GeoJSON format)
  995.                     $coordinatesData json_decode($coordinatesStringtrue);
  996.                     
  997.                     if (json_last_error() === JSON_ERROR_NONE && is_array($coordinatesData)) {
  998.                         // Handle GeoJSON format: coordinates can be in various structures
  999.                         // [[[lng, lat], [lng, lat], ...]] for Polygon
  1000.                         // [[lng, lat], [lng, lat], ...] for LineString
  1001.                         // [lng, lat] for Point
  1002.                         $coordArray = [];
  1003.                         
  1004.                         if (isset($coordinatesData['type']) && $coordinatesData['type'] === 'Polygon' && isset($coordinatesData['coordinates'])) {
  1005.                             // GeoJSON Polygon format
  1006.                             $coordArray $coordinatesData['coordinates'][0] ?? [];
  1007.                         } elseif (isset($coordinatesData['type']) && $coordinatesData['type'] === 'LineString' && isset($coordinatesData['coordinates'])) {
  1008.                             // GeoJSON LineString format
  1009.                             $coordArray $coordinatesData['coordinates'];
  1010.                         } elseif (is_array($coordinatesData) && isset($coordinatesData[0])) {
  1011.                             // Nested array format: [[[lng, lat], ...]] or [[lng, lat], ...]
  1012.                             if (is_array($coordinatesData[0]) && is_array($coordinatesData[0][0])) {
  1013.                                 // [[[lng, lat], ...]] - take first ring
  1014.                                 $coordArray $coordinatesData[0];
  1015.                             } elseif (is_array($coordinatesData[0]) && count($coordinatesData[0]) >= && is_numeric($coordinatesData[0][0])) {
  1016.                                 // [[lng, lat], ...]
  1017.                                 $coordArray $coordinatesData;
  1018.                             }
  1019.                         }
  1020.                         
  1021.                         // Process coordinate array
  1022.                         if (!empty($coordArray)) {
  1023.                             $polygon '';
  1024.                             $firstCoordinate null;
  1025.                             foreach ($coordArray as $coord) {
  1026.                                 if (is_array($coord) && count($coord) >= 2) {
  1027.                                     $lng = (float)$coord[0];
  1028.                                     $lat = (float)$coord[1];
  1029.                                     
  1030.                                     // CAP polygon format: "lat,lng lat,lng ..." (note: lat first, then lng)
  1031.                                     // Format with 6 decimal places as per client requirement
  1032.                                     $coordString number_format($lat6'.''') . "," number_format($lng6'.''');
  1033.                                     
  1034.                                     if ($firstCoordinate === null) {
  1035.                                         $firstCoordinate $coordString;
  1036.                                     }
  1037.                                     $polygon .= $coordString ' ';
  1038.                                 }
  1039.                             }
  1040.                             
  1041.                             // Remove the trailing space
  1042.                             $polygon rtrim($polygon);
  1043.                             
  1044.                             // Validate and ensure polygon is closed
  1045.                             if (!empty(trim($polygon)) && $firstCoordinate !== null) {
  1046.                                 $polygonTrimmed trim($polygon);
  1047.                                 $coords explode(' '$polygonTrimmed);
  1048.                                 $uniqueCoords array_unique($coords);
  1049.                                 
  1050.                                 // Check if we have at least 4 distinct points
  1051.                                 if (count($uniqueCoords) >= 4) {
  1052.                                     // Ensure polygon is closed (first coordinate = last coordinate) - CAP compliance requirement
  1053.                                     $lastCoordinate end($coords);
  1054.                                     if ($lastCoordinate !== $firstCoordinate) {
  1055.                                         $polygon .= ' ' $firstCoordinate;
  1056.                                     }
  1057.                                     // Create a single polygon entry for fallback
  1058.                                     $governoratePolygons[] = [
  1059.                                         'governorate' => null,
  1060.                                         'nameEn' => null,
  1061.                                         'nameAr' => null,
  1062.                                         'polygon' => $polygon
  1063.                                     ];
  1064.                                 }
  1065.                             }
  1066.                         }
  1067.                     } else {
  1068.                         // Fallback: try to parse as string format "[[lat,lng],[lat,lng],...]"
  1069.                         $coordinatesString trim($coordinatesString'[]');
  1070.                         $coordinates explode("],["$coordinatesString);
  1071.                         
  1072.                         $polygon '';
  1073.                         $firstCoordinate null;
  1074.                         foreach ($coordinates as $coordinate) {
  1075.                             $parts explode(","trim($coordinate'[]'));
  1076.                             if (count($parts) >= && is_numeric($parts[0]) && is_numeric($parts[1])) {
  1077.                                 $lat = (float)$parts[0];
  1078.                                 $lng = (float)$parts[1];
  1079.                                 // Format with 6 decimal places as per client requirement
  1080.                                 $coordString number_format($lat6'.''') . "," number_format($lng6'.''');
  1081.                                 
  1082.                                 if ($firstCoordinate === null) {
  1083.                                     $firstCoordinate $coordString;
  1084.                                 }
  1085.                                 $polygon .= $coordString ' ';
  1086.                             }
  1087.                         }
  1088.                         
  1089.                         // Remove the trailing space and validate
  1090.                         $polygon rtrim($polygon);
  1091.                         if (!empty(trim($polygon)) && $firstCoordinate !== null) {
  1092.                             $polygonTrimmed trim($polygon);
  1093.                             $coords explode(' '$polygonTrimmed);
  1094.                             $uniqueCoords array_unique($coords);
  1095.                             
  1096.                             if (count($uniqueCoords) >= 4) {
  1097.                                 $lastCoordinate end($coords);
  1098.                                 if ($lastCoordinate !== $firstCoordinate) {
  1099.                                     $polygon .= ' ' $firstCoordinate;
  1100.                                 }
  1101.                                 $governoratePolygons[] = [
  1102.                                     'governorate' => null,
  1103.                                     'nameEn' => null,
  1104.                                     'nameAr' => null,
  1105.                                     'polygon' => $polygon
  1106.                                 ];
  1107.                             }
  1108.                         }
  1109.                     }
  1110.                 }
  1111.             }
  1112.             
  1113.             // Create area elements - one for each governorate polygon (separate polygons, not merged)
  1114.             // Each governorate gets its own <area> element with its own <polygon>, even if from the same region
  1115.             if (!empty($governoratePolygons)) {
  1116.                 foreach ($governoratePolygons as $govPolygon) {
  1117.                     $area $doc->createElement('area');
  1118.                     $info->appendChild($area);
  1119.                     
  1120.                     // Create areaDesc for this governorate (format: "Region region - GovernorateName")
  1121.                     $areaDescText '';
  1122.                     if ($lang == 'ar') {
  1123.                         $areaDescText "منطقة " $regionName;
  1124.                         if (!empty($govPolygon['nameAr'])) {
  1125.                             $areaDescText .= " - " $govPolygon['nameAr'];
  1126.                         }
  1127.                     } else {
  1128.                         $areaDescText $regionName " " $translator->trans("region");
  1129.                         if (!empty($govPolygon['nameEn'])) {
  1130.                             $areaDescText .= " - " $govPolygon['nameEn'];
  1131.                         }
  1132.                     }
  1133.                     // Note: Removed $notification->getMessage() to match the required format
  1134.                     $areaDesc $doc->createElement('areaDesc'$areaDescText ' : ' $translator->trans('The entire governorate'));
  1135.                     
  1136.                     // Validate and create polygon element for this specific governorate
  1137.                     $polygon $govPolygon['polygon'];
  1138.                     if (!empty(trim($polygon))) {
  1139.                         $polygonTrimmed trim($polygon);
  1140.                         $coords explode(' '$polygonTrimmed);
  1141.                         $uniqueCoords array_unique($coords);
  1142.                         
  1143.                         // Check if we have at least 4 distinct points
  1144.                         if (count($uniqueCoords) >= 4) {
  1145.                             // Ensure polygon is closed (first coordinate = last coordinate) - CAP compliance requirement
  1146.                             $firstCoord $coords[0];
  1147.                             $lastCoord end($coords);
  1148.                             if ($lastCoord !== $firstCoord) {
  1149.                                 $polygon .= ' ' $firstCoord;
  1150.                             }
  1151.                             $polygonElement $doc->createElement('polygon'$polygon);
  1152.                         } else {
  1153.                             // Not enough distinct points - create a bounding box for this specific governorate
  1154.                             // Use governorate-specific coordinates if available
  1155.                             $govSpecificCoords '';
  1156.                             if (is_object($govPolygon['governorate']) && method_exists($govPolygon['governorate'], 'getLatitude')) {
  1157.                                 $govLat $govPolygon['governorate']->getLatitude();
  1158.                                 $govLng $govPolygon['governorate']->getLongitude();
  1159.                                 if (is_numeric($govLat) && is_numeric($govLng)) {
  1160.                                     $govSpecificCoords number_format($govLat6'.''') . ',' number_format($govLng6'.''');
  1161.                                 }
  1162.                             }
  1163.                             $polygonElement $this->createFallbackPolygon($doc$notification$govSpecificCoords$timezone);
  1164.                         }
  1165.                     } else {
  1166.                         // No valid coordinates - create fallback polygon for this specific governorate
  1167.                         $govSpecificCoords '';
  1168.                         if (is_object($govPolygon['governorate']) && method_exists($govPolygon['governorate'], 'getLatitude')) {
  1169.                             $govLat $govPolygon['governorate']->getLatitude();
  1170.                             $govLng $govPolygon['governorate']->getLongitude();
  1171.                             if (is_numeric($govLat) && is_numeric($govLng)) {
  1172.                                 $govSpecificCoords number_format($govLat6'.''') . ',' number_format($govLng6'.''');
  1173.                             }
  1174.                         }
  1175.                         $polygonElement $this->createFallbackPolygon($doc$notification$govSpecificCoords$timezone);
  1176.                     }
  1177.                     
  1178.                     $area->appendChild($areaDesc);
  1179.                     $area->appendChild($polygonElement);
  1180.                 }
  1181.             } else {
  1182.                 // No polygons found - create a single fallback area
  1183.                 $area $doc->createElement('area');
  1184.                 $info->appendChild($area);
  1185.                 
  1186.                 // Create child elements for the <area> element
  1187.                 if ($lang == 'ar') {
  1188.                     $areaDescText "منطقة " $regionName;
  1189.                 } else {
  1190.                     $areaDescText $regionName " " $translator->trans("region");
  1191.                 }
  1192.                 if (!empty($governorateNames)) {
  1193.                     $areaDescText .= " " $translator->trans("including") . " " $governorateNames;
  1194.                 }
  1195.                 $areaDescText .= $notification->getMessage();
  1196.                 $areaDesc $doc->createElement('areaDesc'$areaDescText);
  1197.                 
  1198.                 $polygonElement $this->createFallbackPolygon($doc$notification$governorateCoordinates$timezone);
  1199.                 
  1200.                 $area->appendChild($areaDesc);
  1201.                 $area->appendChild($polygonElement);
  1202.             }
  1203.             // Add XML stylesheet reference (CAP compliance)
  1204.             $stylesheet $doc->createProcessingInstruction('xml-stylesheet''type="text/xsl" href="https://ncm.gov.sa/assets/styles/cap.xsl"');
  1205.             $doc->insertBefore($stylesheet$alert);
  1206.             // Add digital signature structure (placeholder - CAP compliance recommendation)
  1207.             // Note: This is a placeholder. For production, implement real XML digital signatures
  1208.             $dsSignature $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:Signature');
  1209.             
  1210.             // Create SignedInfo element
  1211.             $dsSignedInfo $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:SignedInfo');
  1212.             
  1213.             // CanonicalizationMethod
  1214.             $dsCanonicalizationMethod $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:CanonicalizationMethod');
  1215.             $dsCanonicalizationMethod->setAttribute('Algorithm''http://www.w3.org/2001/10/xml-exc-c14n#');
  1216.             $dsSignedInfo->appendChild($dsCanonicalizationMethod);
  1217.             
  1218.             // SignatureMethod
  1219.             $dsSignatureMethod $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:SignatureMethod');
  1220.             $dsSignatureMethod->setAttribute('Algorithm''http://www.w3.org/2001/04/xmldsig-more#rsa-sha256');
  1221.             $dsSignedInfo->appendChild($dsSignatureMethod);
  1222.             
  1223.             // Reference - Empty URI for enveloped signature (signs the parent document)
  1224.             $dsReference $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:Reference');
  1225.             $dsReference->setAttribute('URI''');
  1226.             
  1227.             // Transforms
  1228.             $dsTransforms $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:Transforms');
  1229.             $dsTransform $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:Transform');
  1230.             $dsTransform->setAttribute('Algorithm''http://www.w3.org/2000/09/xmldsig#enveloped-signature');
  1231.             $dsTransforms->appendChild($dsTransform);
  1232.             $dsReference->appendChild($dsTransforms);
  1233.             
  1234.             // DigestMethod
  1235.             $dsDigestMethod $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:DigestMethod');
  1236.             $dsDigestMethod->setAttribute('Algorithm''http://www.w3.org/2001/04/xmlenc#sha256');
  1237.             $dsReference->appendChild($dsDigestMethod);
  1238.             
  1239.             // DigestValue - Generate hash from notification data (placeholder)
  1240.             $digestData $notification->getGuid() . '_' $notification->getId() . '_' time();
  1241.             $digestHash hash('sha256'$digestDatatrue);
  1242.             $digestValue base64_encode($digestHash);
  1243.             $dsDigestValue $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:DigestValue'$digestValue);
  1244.             $dsReference->appendChild($dsDigestValue);
  1245.             
  1246.             $dsSignedInfo->appendChild($dsReference);
  1247.             $dsSignature->appendChild($dsSignedInfo);
  1248.             
  1249.             // SignatureValue - Generate base64-encoded signature-like value
  1250.             $signatureData $notification->getGuid() . '_' $notification->getId() . '_' time();
  1251.             $hash hash('sha256'$signatureDatatrue);
  1252.             // Generate additional random bytes to create a signature-like length (RSA-2048 signature is ~256 bytes)
  1253.             $randomBytes random_bytes(192); // 192 + 32 (sha256) = 224 bytes, base64 = ~300 chars
  1254.             $combinedBytes $hash $randomBytes;
  1255.             $uniqueSignatureValue base64_encode($combinedBytes);
  1256.             $dsSignatureValue $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:SignatureValue'$uniqueSignatureValue);
  1257.             $dsSignature->appendChild($dsSignatureValue);
  1258.             
  1259.             // KeyInfo
  1260.             $dsKeyInfo $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:KeyInfo');
  1261.             $dsKeyName $doc->createElementNS('http://www.w3.org/2000/09/xmldsig#''ds:KeyName''NCM-Public-Key');
  1262.             $dsKeyInfo->appendChild($dsKeyName);
  1263.             $dsSignature->appendChild($dsKeyInfo);
  1264.             
  1265.             $alert->appendChild($dsSignature);
  1266.             // Save the XML to a file or output it
  1267.             $xmlString $doc->saveXML();
  1268.             print $xmlString;
  1269.             exit;
  1270.             return $xmlString;
  1271.         } catch (\Exception $ex) {
  1272.             $result = ["success" => false"message" => $ex->getMessage()];
  1273.         }
  1274.         return $result;
  1275.     }
  1276.     /**
  1277.      * Get polygon coordinates from JSON lookup sheet for given governorates
  1278.      * 
  1279.      * This method validates and formats latitude/longitude coordinates to exactly 6 decimal places
  1280.      * as required by CAP specification. The JSON file contains the official polygon boundaries
  1281.      * for Saudi Arabia governorates, and coordinates are validated and standardized here.
  1282.      * 
  1283.      * Returns separate polygons for each governorate (not merged)
  1284.      * 
  1285.      * @param array $governorates Array of Governorate objects
  1286.      * @return array Array of arrays with 'governorate', 'nameEn', 'nameAr', and 'polygon' keys
  1287.      */
  1288.     private function getPolygonFromJsonSheet($governorates)
  1289.     {
  1290.         $governoratePolygons = [];
  1291.         // JSON file path containing validated polygon boundaries with coordinates
  1292.         // Use only ksa_governorate_boundaries.geojson file
  1293.         $jsonFilePath PIMCORE_PROJECT_ROOT '/public/import/ksa_governorate_boundaries.geojson';
  1294.         
  1295.         // Check if file exists
  1296.         if (!file_exists($jsonFilePath)) {
  1297.             error_log("getPolygonFromJsonSheet: JSON file not found at: " $jsonFilePath);
  1298.             return [];
  1299.         }
  1300.         
  1301.         // Load JSON file content
  1302.         $jsonContent file_get_contents($jsonFilePath);
  1303.         if ($jsonContent === false) {
  1304.             error_log("getPolygonFromJsonSheet: Failed to read JSON file");
  1305.             return [];
  1306.         }
  1307.         
  1308.         // Try to decode JSON with increased depth limit for large nested structures
  1309.         $jsonData json_decode($jsonContenttrue512JSON_BIGINT_AS_STRING);
  1310.         
  1311.         // If JSON decode fails, try alternative approach: extract coordinates directly from file
  1312.         if (json_last_error() !== JSON_ERROR_NONE) {
  1313.             $errorMsg "getPolygonFromJsonSheet: JSON decode error: " json_last_error_msg() . " (Error code: " json_last_error() . ")";
  1314.             error_log($errorMsg);
  1315.             error_log("getPolygonFromJsonSheet: File size: " strlen($jsonContent) . " bytes");
  1316.             // Fallback: Extract coordinates directly from file content using regex/string matching
  1317.             return $this->extractCoordinatesFromFileContent($jsonContent$governorates);
  1318.         }
  1319.         
  1320.         if (!isset($jsonData['features']) || !is_array($jsonData['features'])) {
  1321.             $availableKeys = isset($jsonData) ? implode(', 'array_keys($jsonData)) : 'null';
  1322.             error_log("getPolygonFromJsonSheet: JSON missing 'features' array. Available keys: " $availableKeys);
  1323.             error_log("getPolygonFromJsonSheet: JSON type: " gettype($jsonData));
  1324.             return [];
  1325.         }
  1326.         
  1327.         $debugMatches = [];
  1328.         $debugInfo null// Initialize debug info variable
  1329.         
  1330.         // Log total features in JSON for debugging
  1331.         error_log("getPolygonFromJsonSheet: Total features in JSON: " count($jsonData['features']));
  1332.         
  1333.         // Process each governorate separately to create individual polygons
  1334.         foreach ($governorates as $governorate) {
  1335.             // Handle both object and array cases
  1336.             if (is_object($governorate)) {
  1337.                 $governorateId method_exists($governorate'getGovernoteId') ? $governorate->getGovernoteId() : null;
  1338.                 $governorateNameEn method_exists($governorate'getName') ? $governorate->getName('en') : null;
  1339.                 $governorateNameAr method_exists($governorate'getName') ? $governorate->getName('ar') : null;
  1340.                 $governorateObj $governorate;
  1341.                 
  1342.                 // Check if this governorate is a municipality and fetch parent governorate
  1343.                 if (method_exists($governorate'getIsMunicipality') && $governorate->getIsMunicipality()) {
  1344.                     $municipalityId method_exists($governorate'getMunicipalityID') ? $governorate->getMunicipalityID() : null;
  1345.                     if ($municipalityId !== null) {
  1346.                         try {
  1347.                             $municipality \Pimcore\Model\DataObject\Municipality::getByMunicipalityid($municipalityIdtrue);
  1348.                             if ($municipality && $municipality->getGovernorate()) {
  1349.                                 $parentGovernorate $municipality->getGovernorate();
  1350.                                 // Use parent governorate's names for matching (English first, then Arabic)
  1351.                                 $governorateNameEn $parentGovernorate->getName('en');
  1352.                                 $governorateNameAr $parentGovernorate->getName('ar');
  1353.                                 // Also update the ID to parent governorate's ID for matching
  1354.                                 $governorateId $parentGovernorate->getGovernoteId();
  1355.                                 error_log("getPolygonFromJsonSheet: Municipality detected (ID: " $municipalityId "), using parent governorate - ID: " var_export($governorateIdtrue) . ", EN: " var_export($governorateNameEntrue) . ", AR: " var_export($governorateNameArtrue));
  1356.                             }
  1357.                         } catch (\Exception $ex) {
  1358.                             error_log("getPolygonFromJsonSheet: Error fetching municipality (ID: " $municipalityId "): " $ex->getMessage());
  1359.                         }
  1360.                     }
  1361.                 }
  1362.             } elseif (is_array($governorate)) {
  1363.                 $governorateId $governorate['id'] ?? $governorate['governoteId'] ?? null;
  1364.                 $governorateNameEn $governorate['name_en'] ?? $governorate['name']['en'] ?? null;
  1365.                 $governorateNameAr $governorate['name_ar'] ?? $governorate['name']['ar'] ?? null;
  1366.                 $governorateObj $governorate;
  1367.                 
  1368.                 // Check if this governorate is a municipality (array format)
  1369.                 if (isset($governorate['IsMunicipality']) && $governorate['IsMunicipality']) {
  1370.                     $municipalityId $governorate['MunicipalityID'] ?? null;
  1371.                     if ($municipalityId !== null) {
  1372.                         try {
  1373.                             $municipality \Pimcore\Model\DataObject\Municipality::getByMunicipalityid($municipalityIdtrue);
  1374.                             if ($municipality && $municipality->getGovernorate()) {
  1375.                                 $parentGovernorate $municipality->getGovernorate();
  1376.                                 // Use parent governorate's names for matching (English first, then Arabic)
  1377.                                 $governorateNameEn $parentGovernorate->getName('en');
  1378.                                 $governorateNameAr $parentGovernorate->getName('ar');
  1379.                                 // Also update the ID to parent governorate's ID for matching
  1380.                                 $governorateId $parentGovernorate->getGovernoteId();
  1381.                                 error_log("getPolygonFromJsonSheet: Municipality detected (ID: " $municipalityId "), using parent governorate - ID: " var_export($governorateIdtrue) . ", EN: " var_export($governorateNameEntrue) . ", AR: " var_export($governorateNameArtrue));
  1382.                             }
  1383.                         } catch (\Exception $ex) {
  1384.                             error_log("getPolygonFromJsonSheet: Error fetching municipality (ID: " $municipalityId "): " $ex->getMessage());
  1385.                         }
  1386.                     }
  1387.                 }
  1388.             } else {
  1389.                 error_log("getPolygonFromJsonSheet: Unexpected governorate type: " gettype($governorate));
  1390.                 continue;
  1391.             }
  1392.             
  1393.             // Log what we're looking for
  1394.             error_log("getPolygonFromJsonSheet: Searching for governorate - ID: " var_export($governorateIdtrue) . ", EN: " var_export($governorateNameEntrue) . ", AR: " var_export($governorateNameArtrue));
  1395.             
  1396.             // Log sample features from JSON (only once) - showing REGION_N_1 field that we match against
  1397.             static $loggedSamples false;
  1398.             if (!$loggedSamples) {
  1399.                 $sampleFeatures = [];
  1400.                 foreach (array_slice($jsonData['features'], 010) as $sampleIdx => $sampleFeature) {
  1401.                     $sampleProps $sampleFeature['properties'] ?? [];
  1402.                     $sampleFeatures[] = [
  1403.                         'REGION_N_1' => $sampleProps['REGION_N_1'] ?? 'N/A',
  1404.                     ];
  1405.                 }
  1406.                 error_log("getPolygonFromJsonSheet: Sample REGION_N_1 values from JSON (first 10): " json_encode($sampleFeaturesJSON_UNESCAPED_UNICODE));
  1407.                 $loggedSamples true;
  1408.             }
  1409.             
  1410.             $matched false;
  1411.             $matchReason '';
  1412.             $matchAttempts = [];
  1413.             $foundAnyMatch false// Track if we found at least one match for this governorate
  1414.             
  1415.             // Find matching feature in JSON by matching governorate nameEn with REGION_N_1 field only
  1416.             foreach ($jsonData['features'] as $featureIndex => $feature) {
  1417.                 $matched false// Reset for each feature
  1418.                 if (!isset($feature['properties']) || !isset($feature['geometry'])) {
  1419.                     continue;
  1420.                 }
  1421.                 
  1422.                 $properties $feature['properties'];
  1423.                 $geometry $feature['geometry'];
  1424.                 
  1425.                 // Match by English name only (exact case-insensitive match - using REGION_N_1 field only)
  1426.                 if ($governorateNameEn !== null) {
  1427.                     $govNameEn trim((string)$governorateNameEn);
  1428.                     if (!empty($govNameEn) && isset($properties['REGION_N_1'])) {
  1429.                         // Normalize the governorate name (lowercase, normalize whitespace)
  1430.                         $govNameNormalized strtolower(preg_replace('/\s+/'' '$govNameEn));
  1431.                         
  1432.                         $jsonNameEn trim((string)$properties['REGION_N_1']);
  1433.                         if (!empty($jsonNameEn)) {
  1434.                             $jsonNameNormalized strtolower(preg_replace('/\s+/'' '$jsonNameEn));
  1435.                             
  1436.                             // Only exact case-insensitive match (normalized)
  1437.                             if ($jsonNameNormalized === $govNameNormalized) {
  1438.                                 $matched true;
  1439.                                 $matchReason 'Name_EN (exact via REGION_N_1): ' $governorateNameEn ' matches ' $jsonNameEn;
  1440.                             }
  1441.                         }
  1442.                     }
  1443.                 }
  1444.                 
  1445.                 if ($matched && isset($geometry['type']) && isset($geometry['coordinates'])) {
  1446.                     error_log("getPolygonFromJsonSheet: Match found! Reason: " $matchReason ", Geometry type: " $geometry['type'] . ", ADMIN: " . ($properties['ADMIN'] ?? 'N/A'));
  1447.                     
  1448.                     // Extract coordinates based on geometry type
  1449.                     $coords $this->extractCoordinatesFromGeometry($geometry);
  1450.                     error_log("getPolygonFromJsonSheet: Extracted " count($coords) . " coordinates");
  1451.                     
  1452.                     if (!empty($coords)) {
  1453.                         // Format coordinates for this governorate separately
  1454.                         $polygonCoords $this->formatCoordinates($coords);
  1455.                         
  1456.                         // Check if we already have a polygon for THIS SPECIFIC governorate
  1457.                         // IMPORTANT: We do NOT merge polygons from different features to avoid creating straight connecting lines.
  1458.                         // If a governorate appears in multiple features, we use only the first match to prevent invalid polygon connections.
  1459.                         $existingIndex null;
  1460.                         foreach ($governoratePolygons as $idx => $existingPoly) {
  1461.                             if (($existingPoly['governorate'] === $governorateObj) || 
  1462.                                 (is_object($governorateObj) && is_object($existingPoly['governorate']) && 
  1463.                                  method_exists($governorateObj'getGovernoteId') && 
  1464.                                  method_exists($existingPoly['governorate'], 'getGovernoteId') &&
  1465.                                  $governorateObj->getGovernoteId() === $existingPoly['governorate']->getGovernoteId())) {
  1466.                                 $existingIndex $idx;
  1467.                                 break;
  1468.                             }
  1469.                         }
  1470.                         
  1471.                         if ($existingIndex !== null) {
  1472.                             // Governorate already has a polygon - skip this match to avoid creating straight connecting lines
  1473.                             // Using only the first match ensures clean polygon boundaries without invalid connections
  1474.                             error_log("getPolygonFromJsonSheet: Skipping duplicate match for governorate ID: " $governorateId " (already has polygon)");
  1475.                         } else {
  1476.                             // Store polygon with governorate info (separate polygon per governorate)
  1477.                             // Each governorate gets its own entry, even if multiple governorates are from the same region
  1478.                             $governoratePolygons[] = [
  1479.                                 'governorate' => $governorateObj,
  1480.                                 'nameEn' => $governorateNameEn,
  1481.                                 'nameAr' => $governorateNameAr,
  1482.                                 'polygon' => $polygonCoords
  1483.                             ];
  1484.                             error_log("getPolygonFromJsonSheet: Successfully added polygon for governorate ID: " $governorateId);
  1485.                         }
  1486.                         
  1487.                         $matchDebug = [
  1488.                             'match_reason' => $matchReason,
  1489.                             'coords_count' => count($coords),
  1490.                             'geometry_type' => $geometry['type'],
  1491.                             'matched' => true
  1492.                         ];
  1493.                         $debugMatches[] = $matchDebug;
  1494.                         if ($debugInfo !== null$debugInfo['matches'][] = $matchDebug;
  1495.                         
  1496.                         $foundAnyMatch true// Mark that we found at least one match
  1497.                         
  1498.                         // Continue searching for more matching features (same governorate can have multiple regions)
  1499.                         // We'll merge all matching polygons for this governorate
  1500.                         continue;
  1501.                     } else {
  1502.                         $errorMsg "Matched but extracted 0 coordinates. Geometry type: " $geometry['type'] . ", Coordinates structure: " json_encode($geometry['coordinates']);
  1503.                         error_log("getPolygonFromJsonSheet: " $errorMsg);
  1504.                         if ($debugInfo !== null) {
  1505.                             $debugInfo['errors'][] = $errorMsg;
  1506.                             $debugInfo['matches'][] = ['match_reason' => $matchReason'matched' => true'coords_extracted' => 0];
  1507.                         }
  1508.                     }
  1509.                 }
  1510.             }
  1511.             
  1512.             if (!$foundAnyMatch) {
  1513.                 error_log("getPolygonFromJsonSheet: No match found for governorate ID: " var_export($governorateIdtrue) . ", EN: " var_export($governorateNameEntrue) . ", AR: " var_export($governorateNameArtrue));
  1514.                 $noMatchDebug = [
  1515.                     'governorate_id' => $governorateId,
  1516.                     'governorate_id_type' => gettype($governorateId),
  1517.                     'name_en' => $governorateNameEn,
  1518.                     'name_ar' => $governorateNameAr,
  1519.                     'matched' => false,
  1520.                     'match_attempts' => array_slice($matchAttempts010// Limit to first 10 attempts
  1521.                 ];
  1522.                 $debugMatches[] = $noMatchDebug;
  1523.                 if ($debugInfo !== null$debugInfo['matches'][] = $noMatchDebug;
  1524.             }
  1525.         }
  1526.         
  1527.         // Log summary
  1528.         error_log("getPolygonFromJsonSheet: Processed " count($governorates) . " governorates, found " count($governoratePolygons) . " polygons");
  1529.         
  1530.         if (empty($governoratePolygons)) {
  1531.             // Log available REGION_N_1 values from JSON for debugging
  1532.             $availableRegionNames = [];
  1533.             foreach ($jsonData['features'] as $feature) {
  1534.                 $props $feature['properties'] ?? [];
  1535.                 $regionName $props['REGION_N_1'] ?? null;
  1536.                 if ($regionName !== null) {
  1537.                     $availableRegionNames[] = $regionName;
  1538.                 }
  1539.             }
  1540.             error_log("getPolygonFromJsonSheet: No coordinates found. Debug matches: " json_encode($debugMatches));
  1541.             error_log("getPolygonFromJsonSheet: Available REGION_N_1 values in JSON (first 20): " implode(', 'array_slice($availableRegionNames020)));
  1542.         } else {
  1543.             error_log("getPolygonFromJsonSheet: Successfully extracted polygons for " count($governoratePolygons) . " governorates");
  1544.         }
  1545.         
  1546.         return $governoratePolygons;
  1547.     }
  1548.     
  1549.     /**
  1550.      * Extract coordinates from GeoJSON geometry (supports Polygon and MultiPolygon)
  1551.      * 
  1552.      * @param array $geometry GeoJSON geometry object
  1553.      * @return array Array of coordinate pairs [lng, lat]
  1554.      */
  1555.     private function extractCoordinatesFromGeometry($geometry)
  1556.     {
  1557.         $coordinates = [];
  1558.         
  1559.         if (!isset($geometry['type']) || !isset($geometry['coordinates'])) {
  1560.             return $coordinates;
  1561.         }
  1562.         
  1563.         $type $geometry['type'];
  1564.         $coords $geometry['coordinates'];
  1565.         
  1566.         if ($type === 'Polygon' && is_array($coords)) {
  1567.             // Polygon: coordinates[0] is the outer ring
  1568.             if (isset($coords[0]) && is_array($coords[0])) {
  1569.                 foreach ($coords[0] as $coord) {
  1570.                     if (is_array($coord) && count($coord) >= 2) {
  1571.                         $coordinates[] = $coord;
  1572.                     }
  1573.                 }
  1574.             }
  1575.         } elseif ($type === 'MultiPolygon' && is_array($coords)) {
  1576.             // MultiPolygon: array of polygons, each polygon has coordinates[0] as outer ring
  1577.             // IMPORTANT: Use only the FIRST polygon to avoid creating straight connecting lines between disconnected parts
  1578.             // CAP XML polygons should represent a single continuous boundary
  1579.             if (!empty($coords) && is_array($coords[0]) && isset($coords[0][0]) && is_array($coords[0][0])) {
  1580.                 foreach ($coords[0][0] as $coord) {
  1581.                     if (is_array($coord) && count($coord) >= 2) {
  1582.                         $coordinates[] = $coord;
  1583.                     }
  1584.                 }
  1585.             }
  1586.         }
  1587.         
  1588.         return $coordinates;
  1589.     }
  1590.     
  1591.     /**
  1592.      * Extract coordinates directly from file content when JSON parsing fails
  1593.      * This is a fallback method that uses string/regex matching
  1594.      */
  1595.     private function extractCoordinatesFromFileContent($fileContent$governorates)
  1596.     {
  1597.         $governoratePolygons = [];
  1598.         
  1599.         foreach ($governorates as $governorate) {
  1600.             $governorateCoords = [];
  1601.             // Get governorate ID and names
  1602.             if (is_object($governorate)) {
  1603.                 $governorateId method_exists($governorate'getGovernoteId') ? $governorate->getGovernoteId() : null;
  1604.                 $governorateNameEn method_exists($governorate'getName') ? $governorate->getName('en') : null;
  1605.                 $governorateNameAr method_exists($governorate'getName') ? $governorate->getName('ar') : null;
  1606.                 $governorateObj $governorate;
  1607.                 
  1608.                 // Check if this governorate is a municipality and fetch parent governorate
  1609.                 if (method_exists($governorate'getIsMunicipality') && $governorate->getIsMunicipality()) {
  1610.                     $municipalityId method_exists($governorate'getMunicipalityID') ? $governorate->getMunicipalityID() : null;
  1611.                     if ($municipalityId !== null) {
  1612.                         try {
  1613.                             $municipality \Pimcore\Model\DataObject\Municipality::getByMunicipalityid($municipalityIdtrue);
  1614.                             if ($municipality && $municipality->getGovernorate()) {
  1615.                                 $parentGovernorate $municipality->getGovernorate();
  1616.                                 // Use parent governorate's names for matching (English first, then Arabic)
  1617.                                 $governorateNameEn $parentGovernorate->getName('en');
  1618.                                 $governorateNameAr $parentGovernorate->getName('ar');
  1619.                                 // Also update the ID to parent governorate's ID for matching
  1620.                                 $governorateId $parentGovernorate->getGovernoteId();
  1621.                                 error_log("extractCoordinatesFromFileContent: Municipality detected (ID: " $municipalityId "), using parent governorate - ID: " var_export($governorateIdtrue) . ", EN: " var_export($governorateNameEntrue) . ", AR: " var_export($governorateNameArtrue));
  1622.                             }
  1623.                         } catch (\Exception $ex) {
  1624.                             error_log("extractCoordinatesFromFileContent: Error fetching municipality (ID: " $municipalityId "): " $ex->getMessage());
  1625.                         }
  1626.                     }
  1627.                 }
  1628.             } elseif (is_array($governorate)) {
  1629.                 $governorateId $governorate['id'] ?? $governorate['governoteId'] ?? null;
  1630.                 $governorateNameEn $governorate['name_en'] ?? $governorate['name']['en'] ?? null;
  1631.                 $governorateNameAr $governorate['name_ar'] ?? $governorate['name']['ar'] ?? null;
  1632.                 $governorateObj $governorate;
  1633.                 
  1634.                 // Check if this governorate is a municipality (array format)
  1635.                 if (isset($governorate['IsMunicipality']) && $governorate['IsMunicipality']) {
  1636.                     $municipalityId $governorate['MunicipalityID'] ?? null;
  1637.                     if ($municipalityId !== null) {
  1638.                         try {
  1639.                             $municipality \Pimcore\Model\DataObject\Municipality::getByMunicipalityid($municipalityIdtrue);
  1640.                             if ($municipality && $municipality->getGovernorate()) {
  1641.                                 $parentGovernorate $municipality->getGovernorate();
  1642.                                 // Use parent governorate's names for matching (English first, then Arabic)
  1643.                                 $governorateNameEn $parentGovernorate->getName('en');
  1644.                                 $governorateNameAr $parentGovernorate->getName('ar');
  1645.                                 // Also update the ID to parent governorate's ID for matching
  1646.                                 $governorateId $parentGovernorate->getGovernoteId();
  1647.                                 error_log("extractCoordinatesFromFileContent: Municipality detected (ID: " $municipalityId "), using parent governorate - ID: " var_export($governorateIdtrue) . ", EN: " var_export($governorateNameEntrue) . ", AR: " var_export($governorateNameArtrue));
  1648.                             }
  1649.                         } catch (\Exception $ex) {
  1650.                             error_log("extractCoordinatesFromFileContent: Error fetching municipality (ID: " $municipalityId "): " $ex->getMessage());
  1651.                         }
  1652.                     }
  1653.                 }
  1654.             } else {
  1655.                 continue;
  1656.             }
  1657.             
  1658.             if ($governorateId === null) {
  1659.                 continue;
  1660.             }
  1661.             
  1662.             // Search for the governorate section in the file
  1663.             // Try multiple patterns for both old (GovID) and new (ADMIN) formats
  1664.             // Patterns: "GovID": "76", "GovID": 76, "ADMIN": 12, "ADMIN": "12"
  1665.             $idStr = (string)$governorateId;
  1666.             $patterns = [
  1667.                 '"ADMIN"\s*:\s*"' preg_quote($idStr'/') . '"',
  1668.                 '"ADMIN"\s*:\s*' preg_quote($idStr'/'),
  1669.                 '"GovID"\s*:\s*"' preg_quote($idStr'/') . '"',
  1670.                 '"GovID"\s*:\s*' preg_quote($idStr'/'),
  1671.             ];
  1672.             
  1673.             $found false;
  1674.             foreach ($patterns as $pattern) {
  1675.                 if (preg_match('/' $pattern '/'$fileContent$matchesPREG_OFFSET_CAPTURE)) {
  1676.                     $matchPos $matches[0][1];
  1677.                     $found true;
  1678.                     
  1679.                     // Find the coordinates array that follows this ADMIN/GovID (within next 100000 chars)
  1680.                     $searchArea substr($fileContent$matchPos100000);
  1681.                     
  1682.                     // Look for coordinates array: "coordinates": [[[lng, lat], ...]]
  1683.                     if (preg_match('/"coordinates"\s*:\s*\[/'$searchArea$coordMatchPREG_OFFSET_CAPTURE)) {
  1684.                         $coordStartInArea $coordMatch[0][1];
  1685.                         $coordStart $matchPos $coordStartInArea;
  1686.                         
  1687.                         // Extract the coordinates array by finding matching brackets
  1688.                         $depth 0;
  1689.                         $startBracket false;
  1690.                         $coordString '';
  1691.                         $bracketCount 0;
  1692.                         
  1693.                         // Find the opening bracket
  1694.                         for ($i $coordStart$i strlen($fileContent) && $i $coordStart 200000$i++) {
  1695.                             $char $fileContent[$i];
  1696.                             if ($char === '[') {
  1697.                                 if (!$startBracket) {
  1698.                                     $startBracket true;
  1699.                                 }
  1700.                                 $depth++;
  1701.                                 $bracketCount++;
  1702.                                 $coordString .= $char;
  1703.                             } elseif ($char === ']') {
  1704.                                 $depth--;
  1705.                                 $bracketCount++;
  1706.                                 $coordString .= $char;
  1707.                                 if ($depth === && $startBracket) {
  1708.                                     break; // Found complete coordinates array
  1709.                                 }
  1710.                             } elseif ($startBracket) {
  1711.                                 $coordString .= $char;
  1712.                             } elseif (preg_match('/\S/'$char)) {
  1713.                                 // Non-whitespace before bracket - might be start
  1714.                                 if ($char === '[') {
  1715.                                     $startBracket true;
  1716.                                     $depth 1;
  1717.                                     $coordString '[';
  1718.                                 }
  1719.                             }
  1720.                         }
  1721.                         
  1722.                         // Extract coordinates using regex (more reliable than JSON parsing for malformed JSON)
  1723.                         // Pattern: [ lng, lat ] or [lng,lat] - matches coordinate pairs
  1724.                         $coordPattern '/\[\s*([+-]?\d+\.?\d*)\s*,\s*([+-]?\d+\.?\d*)\s*\]/';
  1725.                         preg_match_all($coordPattern$coordString$coordMatchesPREG_SET_ORDER);
  1726.                         
  1727.                             if (!empty($coordMatches)) {
  1728.                             foreach ($coordMatches as $match) {
  1729.                                 $lng = (float)$match[1];
  1730.                                 $lat = (float)$match[2];
  1731.                                 
  1732.                                 // Validate ranges
  1733.                                 if ($lat >= -90 && $lat <= 90 && $lng >= -180 && $lng <= 180) {
  1734.                                     $governorateCoords[] = [$lng$lat];
  1735.                                 }
  1736.                             }
  1737.                         } else {
  1738.                             // Fallback: Try JSON decode if regex fails
  1739.                             $coordsArray json_decode($coordStringtrue);
  1740.                             if ($coordsArray !== null && is_array($coordsArray)) {
  1741.                                 $coords $this->extractCoordsFromArray($coordsArray);
  1742.                                 if (!empty($coords)) {
  1743.                                     $governorateCoords $coords;
  1744.                                 }
  1745.                             }
  1746.                         }
  1747.                         
  1748.                         // Format coordinates for this governorate separately
  1749.                         if (!empty($governorateCoords)) {
  1750.                             $polygonCoords $this->formatCoordinates($governorateCoords);
  1751.                             $governoratePolygons[] = [
  1752.                                 'governorate' => $governorateObj,
  1753.                                 'nameEn' => $governorateNameEn,
  1754.                                 'nameAr' => $governorateNameAr,
  1755.                                 'polygon' => $polygonCoords
  1756.                             ];
  1757.                         }
  1758.                     }
  1759.                     break; // Found match, no need to try other patterns
  1760.                 }
  1761.             }
  1762.         }
  1763.         
  1764.         return $governoratePolygons;
  1765.     }
  1766.     
  1767.     /**
  1768.      * Extract coordinate pairs from nested array structure (Polygon/MultiPolygon)
  1769.      */
  1770.     private function extractCoordsFromArray($coordsArray)
  1771.     {
  1772.         $coordinates = [];
  1773.         
  1774.         if (!is_array($coordsArray)) {
  1775.             return $coordinates;
  1776.         }
  1777.         
  1778.         // Check if it's a coordinate pair [lng, lat]
  1779.         if (count($coordsArray) === && is_numeric($coordsArray[0]) && is_numeric($coordsArray[1])) {
  1780.             $coordinates[] = $coordsArray;
  1781.             return $coordinates;
  1782.         }
  1783.         
  1784.         // Recursively process nested arrays
  1785.         foreach ($coordsArray as $item) {
  1786.             if (is_array($item)) {
  1787.                 $subCoords $this->extractCoordsFromArray($item);
  1788.                 $coordinates array_merge($coordinates$subCoords);
  1789.             }
  1790.         }
  1791.         
  1792.         return $coordinates;
  1793.     }
  1794.     
  1795.     /**
  1796.      * Format coordinates for CAP format
  1797.      */
  1798.     private function formatCoordinates($allCoordinates)
  1799.     {
  1800.         $formattedCoords = [];
  1801.         
  1802.         foreach ($allCoordinates as $coord) {
  1803.             if (is_array($coord) && count($coord) >= 2) {
  1804.                 if (!is_numeric($coord[0]) || !is_numeric($coord[1])) {
  1805.                     continue;
  1806.                 }
  1807.                 
  1808.                 $lng = (float)$coord[0];
  1809.                 $lat = (float)$coord[1];
  1810.                 
  1811.                 // Validate ranges
  1812.                 if ($lat < -90 || $lat 90 || $lng < -180 || $lng 180) {
  1813.                     continue;
  1814.                 }
  1815.                 
  1816.                 $formattedLat number_format($lat6'.''');
  1817.                 $formattedLng number_format($lng6'.''');
  1818.                 $formattedCoords[] = $formattedLat ',' $formattedLng;
  1819.             }
  1820.         }
  1821.         
  1822.         if (!empty($formattedCoords)) {
  1823.             $polygonCoords implode(' '$formattedCoords);
  1824.             
  1825.             // Ensure polygon is closed
  1826.             $coordsArray explode(' '$polygonCoords);
  1827.             $firstCoord $coordsArray[0];
  1828.             $lastCoord end($coordsArray);
  1829.             if ($firstCoord !== $lastCoord) {
  1830.                 $polygonCoords .= ' ' $firstCoord;
  1831.             }
  1832.             
  1833.             return $polygonCoords;
  1834.         }
  1835.         
  1836.         return '';
  1837.     }
  1838.     /**
  1839.      * Create a fallback polygon when coordinates are invalid or missing
  1840.      * Creates a bounding box from region/governorate coordinates
  1841.      */
  1842.     private function createFallbackPolygon($doc$notification$governorateCoordinates$timezone)
  1843.     {
  1844.         $polygonCoords '';
  1845.         
  1846.         // Try to use governorate coordinates first
  1847.         if (!empty($governorateCoordinates)) {
  1848.             $coords explode(' 'trim($governorateCoordinates));
  1849.             $validCoords = [];
  1850.             
  1851.             foreach ($coords as $coord) {
  1852.                 if (strpos($coord',') !== false) {
  1853.                     $parts explode(','$coord);
  1854.                     if (count($parts) == && is_numeric($parts[0]) && is_numeric($parts[1])) {
  1855.                         $validCoords[] = $coord;
  1856.                     }
  1857.                 }
  1858.             }
  1859.             
  1860.             if (count($validCoords) >= 4) {
  1861.                 // Use governorate coordinates
  1862.                 $polygonCoords implode(' '$validCoords);
  1863.                 // Close the polygon
  1864.                 if (!empty($validCoords)) {
  1865.                     $firstCoord $validCoords[0];
  1866.                     $lastCoord end($validCoords);
  1867.                     if ($firstCoord !== $lastCoord) {
  1868.                         $polygonCoords .= ' ' $firstCoord;
  1869.                     }
  1870.                 }
  1871.             }
  1872.         }
  1873.         
  1874.         // Fallback to region coordinates if governorate coordinates are insufficient
  1875.         if (empty($polygonCoords) && $notification->getRegion()) {
  1876.             $lat $notification->getRegion()->getLatitude();
  1877.             $lng $notification->getRegion()->getLongitude();
  1878.             
  1879.             if (is_numeric($lat) && is_numeric($lng)) {
  1880.                 // Create a small bounding box around the region center (0.1 degree radius)
  1881.                 $offset 0.1;
  1882.                 $coords = [
  1883.                     ($lat $offset) . ',' . ($lng $offset), // SW
  1884.                     ($lat $offset) . ',' . ($lng $offset), // SE
  1885.                     ($lat $offset) . ',' . ($lng $offset), // NE
  1886.                     ($lat $offset) . ',' . ($lng $offset), // NW
  1887.                     ($lat $offset) . ',' . ($lng $offset)  // Close polygon
  1888.                 ];
  1889.                 $polygonCoords implode(' '$coords);
  1890.             }
  1891.         }
  1892.         
  1893.         // Final fallback: use default coordinates if nothing else works
  1894.         if (empty($polygonCoords)) {
  1895.             // Default coordinates for Saudi Arabia center (Riyadh area)
  1896.             $defaultCoords = [
  1897.                 '24.5,46.5',  // SW
  1898.                 '24.5,46.7',  // SE
  1899.                 '24.7,46.7',  // NE
  1900.                 '24.7,46.5',  // NW
  1901.                 '24.5,46.5'   // Close polygon
  1902.             ];
  1903.             $polygonCoords implode(' '$defaultCoords);
  1904.         }
  1905.         
  1906.         return $doc->createElement('polygon'$polygonCoords);
  1907.     }
  1908.     public function viewNotification($params$translator): array
  1909.     {
  1910.         $result = [];
  1911.         // try {
  1912.         $viewNotification DataObject\EwsNotification::getById($params['id'], false);
  1913.         if (!$viewNotification) {
  1914.             $viewNotification DataObject\EwsNotification::getById($params['id'], true);
  1915.         }
  1916.         if ($viewNotification) {
  1917.             $notificationData $this->createNotificationFormat($viewNotification$translator);
  1918.             $history =  $this->getVersions($viewNotification$translator);
  1919.             return ["success" => true"data" => $notificationData"history" => $history];
  1920.         }
  1921.         return ["success" => false"message" => $translator->trans("ews_notification_does_not_exists")];
  1922.         // } catch (\Exception $ex) {
  1923.         //     $result = ["success" => false, "message" => $ex->getMessage()];
  1924.         // }
  1925.         return $result;
  1926.     }
  1927.     public function addAddressComponentsFieldCollection($addressComponentsDataObject\EwsNotification $object)
  1928.     {
  1929.         $items = new \Pimcore\Model\DataObject\Fieldcollection();
  1930.         foreach ($addressComponents as $Data) {
  1931.             #creating field collections
  1932.             $item = new DataObject\Fieldcollection\Data\AddressComponents();
  1933.             $item->setAddressValue(strip_tags($Data['long_name']));
  1934.             $item->setAddressKey(strip_tags($Data['types'][0]));
  1935.             $items->add($item);
  1936.         }
  1937.         return $object->setAddressComponents($items);
  1938.     }
  1939.     public function getAlertAction($id)
  1940.     {
  1941.         $alertActionObj DataObject\AlertAction::getById($id);
  1942.         $data = [];
  1943.         if (!empty($alertActionObj)) {
  1944.             $data['id'] = $alertActionObj->getAlertActionId();
  1945.             $data['severity'] = $alertActionObj->getSeverity();
  1946.             $data['nameEn'] = $alertActionObj->getName('en');
  1947.             $data['nameAr'] = $alertActionObj->getName('ar');
  1948.         }
  1949.         return $data;
  1950.     }
  1951.     public function getAlertType($id)
  1952.     {
  1953.         $alertTypeObj DataObject\AlertType::getById($id);
  1954.         $data = [];
  1955.         if (!empty($alertTypeObj)) {
  1956.             $data['id'] = $alertTypeObj->getAlertTypeId();
  1957.             $data['nameEn'] = $alertTypeObj->getName('en');
  1958.             $data['nameAr'] = $alertTypeObj->getName('ar');
  1959.         }
  1960.         return $data;
  1961.     }
  1962.     public function getAlertStatus($id)
  1963.     {
  1964.         $alertStatus DataObject\AlertStatus::getById($id);
  1965.         $data = [];
  1966.         if (!empty($alertStatus)) {
  1967.             $alertType $alertStatus->getAlertType();
  1968.             $alertData = [];
  1969.             if (!empty($alertType)) {
  1970.                 $alertData = [
  1971.                     "id" => $alertType->getAlertTypeId(),
  1972.                     "nameEn" => $alertType->getName('en'),
  1973.                     "nameAr" => $alertType->getName('ar')
  1974.                 ];
  1975.             }
  1976.             $data['id'] = $alertStatus->getAlertStatusId();
  1977.             $data['nameEn'] = $alertStatus->getName('en');
  1978.             $data['nameAr'] = $alertStatus->getName('ar');
  1979.             $data['alertType'] = $alertData;
  1980.         }
  1981.         return $data;
  1982.     }
  1983.     public function getAlertHazard($id)
  1984.     {
  1985.         $alertHazardObj DataObject\AlertHazard::getById($id);
  1986.         $data = [];
  1987.         if (!empty($alertHazardObj)) {
  1988.             $data['id'] = $alertHazardObj->getAlertHazardId();
  1989.             $data['nameEn'] = $alertHazardObj->getName('en');
  1990.             $data['nameAr'] = $alertHazardObj->getName('ar');
  1991.         }
  1992.         return $data;
  1993.     }
  1994.     public function getRegion($id)
  1995.     {
  1996.         $regionObj DataObject\Region::getById($id);
  1997.         $data = [];
  1998.         if (!empty($regionObj)) {
  1999.             $data['id'] = $regionObj->getRegionId();
  2000.             $data['nameEn'] = $regionObj->getName('en');
  2001.             $data['nameAr'] = $regionObj->getName('ar');
  2002.             $data['longitude'] = $regionObj->getLongitude();
  2003.             $data['latitude'] = $regionObj->getLatitude();
  2004.         }
  2005.         return $data;
  2006.     }
  2007.     public function getEvent($id)
  2008.     {
  2009.         $eventObj DataObject\Event::getById($id);
  2010.         $data = [];
  2011.         if (!empty($eventObj)) {
  2012.             $data['id'] = $eventObj->getEventId();
  2013.             $data['nameEn'] = $eventObj->getName('en');
  2014.             $data['nameAr'] = $eventObj->getName('ar');
  2015.         }
  2016.         return $data;
  2017.     }
  2018.     public function getWeatherPhenomenon($id)
  2019.     {
  2020.         $weatherPhenObj DataObject\PhenomenaList::getById($id);
  2021.         $data = [];
  2022.         if (!empty($weatherPhenObj)) {
  2023.             $data['id'] = $weatherPhenObj->getPhenomenaListId();
  2024.             $data['nameEn'] = $weatherPhenObj->getTitle('en');
  2025.             $data['nameAr'] = $weatherPhenObj->getTitle('ar');
  2026.         }
  2027.         return $data;
  2028.     }
  2029.     public function getWeatherPhenAffect($id)
  2030.     {
  2031.         $weatherPhenObj DataObject\WeatherPhenomenonAffect::getById($id);
  2032.         $data = [];
  2033.         if (!empty($weatherPhenObj)) {
  2034.             $data['id'] = $weatherPhenObj->getWeatherPhenomenonAffectId();
  2035.             $data['nameEn'] = $weatherPhenObj->getName('en');
  2036.             $data['nameAr'] = $weatherPhenObj->getName('ar');
  2037.         }
  2038.         return $data;
  2039.     }
  2040.     // public function getGovernorateDetail($governorateList)
  2041.     // {
  2042.     //     $result = [];
  2043.     //     if (!empty($governorateList)) {
  2044.     //         for ($i = 0; $i < count($governorateList); $i++) {
  2045.     //             $parentGovernates = null;
  2046.     //             if ($governorateList[$i]->getIsMunicipality()) {
  2047.     //                 $municipatlity = \Pimcore\Model\DataObject\Municipality::getByMunicipalityId($governorateList[$i]->getMunicipalityID(), true);
  2048.     //                 if ($municipatlity &&  $municipatlity->getGovernorate()) {
  2049.     //                     $parentGovernates = $municipatlity->getGovernorate();
  2050.     //                 }
  2051.     //             }
  2052.     //             $result[$i]['id'] = $governorateList[$i]->getGovernoteId();
  2053.     //             $result[$i]['nameEn'] = $governorateList[$i]->getName('en');
  2054.     //             $result[$i]['nameAr'] = $governorateList[$i]->getName('ar');
  2055.     //             $result[$i]['longitude'] = $governorateList[$i]->getLongitude();
  2056.     //             $result[$i]['latitude'] = $governorateList[$i]->getLatitude();
  2057.     //             $result[$i]['parentId'] = $parentGovernates ? $parentGovernates->getGovernoteId() : null;
  2058.     //             $result[$i]['parenNameEn'] = $parentGovernates ? $parentGovernates->getName('en') : null;
  2059.     //             $result[$i]['parenNameAr'] = $parentGovernates ? $parentGovernates->getName('ar') : null;
  2060.     //             $result[$i]['parentLongitude'] = $parentGovernates ? $parentGovernates->getLongitude() : null;
  2061.     //             $result[$i]['parentLatitude'] = $parentGovernates ? $parentGovernates->getLatitude() : null;
  2062.     //             $result[$i]['isMunicipality'] = $governorateList[$i]->getIsMunicipality();
  2063.     //             $result[$i]['municipalities'] = [];
  2064.     //         }
  2065.     //     }
  2066.     //     return $result;
  2067.     // }
  2068.     public function getGovernorateDetail($governorateList)
  2069.     {
  2070.         $result = [];
  2071.         if (!empty($governorateList)) {
  2072.             $uniqueGovernorateArray = [];
  2073.             foreach ($governorateList as $governorate) {
  2074.                 $parentGovernates null;
  2075.                 // Check if it is a municipality and fetch parent governorate
  2076.                 if ($governorate->getIsMunicipality()) {
  2077.                     $municipality \Pimcore\Model\DataObject\Municipality::getByMunicipalityId($governorate->getMunicipalityID(), true);
  2078.                     if ($municipality && $municipality->getGovernorate()) {
  2079.                         $parentGovernates $municipality->getGovernorate();
  2080.                     }
  2081.                 }
  2082.                 // Initialize a unique governorate array if needed
  2083.                 $parentId $parentGovernates $parentGovernates->getGovernoteId() : $governorate->getGovernoteId();
  2084.                 if (!isset($uniqueGovernorateArray[$parentId])) {
  2085.                     $uniqueGovernorateArray[$parentId] = [
  2086.                         'id' => $parentId,
  2087.                         'nameEn' => $parentGovernates $parentGovernates->getName('en') : $governorate->getName('en'),
  2088.                         'nameAr' => $parentGovernates $parentGovernates->getName('ar') : $governorate->getName('ar'),
  2089.                         'longitude' => $parentGovernates $parentGovernates->getLongitude() : $governorate->getLongitude(),
  2090.                         'latitude' => $parentGovernates $parentGovernates->getLatitude() : $governorate->getLatitude(),
  2091.                         'municipalities' => []
  2092.                     ];
  2093.                 }
  2094.                 // Add municipality details if it is a municipality
  2095.                 if ($governorate->getIsMunicipality()) {
  2096.                     $uniqueGovernorateArray[$parentId]['municipalities'][] = [
  2097.                         'id' => $governorate->getGovernoteId(),
  2098.                         'nameEn' => $governorate->getName('en'),
  2099.                         'nameAr' => $governorate->getName('ar'),
  2100.                         'longitude' => $governorate->getLongitude(),
  2101.                         'latitude' => $governorate->getLatitude()
  2102.                     ];
  2103.                 }
  2104.             }
  2105.             // Flatten the associative array to a numerical array for output
  2106.             $result array_values($uniqueGovernorateArray);
  2107.         }
  2108.         return $result;
  2109.     }
  2110.     public function getMessageDetail($notificationObj)
  2111.     {
  2112.         $data = [];
  2113.         if (!empty($notificationObj)) {
  2114.             $data['messageEn'] = $notificationObj->getMessage('en');
  2115.             $data['messageAr'] = $notificationObj->getMessage('ar');
  2116.         }
  2117.         return $data;
  2118.     }
  2119.     public function createAsset($fileData$filename)
  2120.     {
  2121.         // Validate the file data
  2122.         if (preg_match('/^data:(image\/(png|jpe?g)|application\/(pdf|vnd.openxmlformats-officedocument.wordprocessingml.document|vnd.ms-excel));base64,/'$fileData) !== 1) {
  2123.             return false// Invalid file data or MIME type
  2124.         }
  2125.         // Extract the file extension from the MIME type
  2126.         $extension '';
  2127.         if (preg_match('/^data:image\/(png|jpe?g);base64,/'$fileData)) {
  2128.             $extension 'jpg'// Assume JPG for base64-encoded image data (PNG or JPEG)
  2129.         } elseif (preg_match('/^data:application\/(pdf|vnd.openxmlformats-officedocument.wordprocessingml.document|vnd.ms-excel);base64,/'$fileData)) {
  2130.             $extension 'pdf'// PDF, DOCX, or Excel
  2131.         }
  2132.         // Remove the "data:image/png;base64," or "data:application/pdf;base64," prefix to get the actual base64-encoded content
  2133.         $base64Content preg_replace('/^data:(image\/png|application\/(pdf|vnd.openxmlformats-officedocument.wordprocessingml.document|vnd.ms-excel));base64,/'''$fileData);
  2134.         // Decode the base64-encoded content
  2135.         $fileContent base64_decode($base64Content);
  2136.         // Validate the decoded content
  2137.         if ($fileContent === false) {
  2138.             return false// Invalid base64-encoded content
  2139.         }
  2140.         // Create the Pimcore asset
  2141.         $asset = new \Pimcore\Model\Asset();
  2142.         $parent Asset\Service::createFolderByPath('/EWSNotification');
  2143.         $asset->setFilename($filename); // Replace with the desired filename and extension
  2144.         $asset->setParent($parent);
  2145.         $asset->setData($fileContent);
  2146.         // Set the MIME type based on the file extension
  2147.         $mimeType '';
  2148.         if ($extension === 'pdf') {
  2149.             $mimeType 'application/pdf';
  2150.         } elseif ($extension === 'jpg') {
  2151.             $mimeType 'image/jpeg';
  2152.         } elseif ($extension === 'png') {
  2153.             $mimeType 'image/png';
  2154.         } elseif ($extension === 'docx') {
  2155.             $mimeType 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
  2156.         } elseif ($extension === 'xlsx') {
  2157.             $mimeType 'application/vnd.ms-excel';
  2158.         }
  2159.         // Validate the MIME type
  2160.         if (!in_array($mimeType, ['image/jpeg''image/png''application/pdf''application/vnd.openxmlformats-officedocument.wordprocessingml.document''application/vnd.ms-excel'])) {
  2161.             return false// Invalid MIME type
  2162.         }
  2163.         $asset->setType($mimeType);
  2164.         // Save the asset
  2165.         $asset->save();
  2166.         return $asset;
  2167.     }
  2168.     public function getPhenomenaListByAlertId($alertID$lang "en")
  2169.     {
  2170.         $response = [];
  2171.         $alertType \Pimcore\Model\DataObject\AlertType::getByAlertTypeId($alertIDtrue);
  2172.         if ($alertType instanceof \Pimcore\Model\DataObject\AlertType) {
  2173.             $phenomenaList $alertType->getPhenomenaList();
  2174.             if ($phenomenaList) {
  2175.                 foreach ($phenomenaList as $phenomena) {
  2176.                     $phenomenaId $phenomena->getPhenomena();
  2177.                     if ($phenomenaId) {
  2178.                         $phenomenaObj \Pimcore\Model\DataObject::getById($phenomenaId);
  2179.                         $response[] = ["id" => $phenomenaObj->getphenomenaListId(), "nameEn" => $phenomenaObj->getTitle("en"), "nameAr" => $phenomenaObj->getTitle("ar"), "criteria" => $phenomena->getCriteria()];
  2180.                     }
  2181.                 }
  2182.             } else {
  2183.                 return ["success" => false"message" => "No criteria is set"];
  2184.             }
  2185.         } else {
  2186.             return ["success" => false"message" => "Invalid alert type id"];
  2187.         }
  2188.         return ["success" => true"data" => $response];
  2189.     }
  2190.     public function getPhenomenaListByAlertIds($alertIDs$lang "en")
  2191.     {
  2192.         $response = [];
  2193.         if (count($alertIDs) > 0) {
  2194.             foreach ($alertIDs as $alertID) {
  2195.                 $alertType \Pimcore\Model\DataObject\AlertType::getByAlertTypeId($alertIDtrue);
  2196.                 if ($alertType instanceof \Pimcore\Model\DataObject\AlertType) {
  2197.                     $phenomenaList $alertType->getPhenomenaList();
  2198.                     if ($phenomenaList) {
  2199.                         foreach ($phenomenaList as $phenomena) {
  2200.                             $phenomenaId $phenomena->getPhenomena();
  2201.                             if ($phenomenaId) {
  2202.                                 $phenomenaObj \Pimcore\Model\DataObject::getById($phenomenaId);
  2203.                                 if ($phenomenaObj instanceof \Pimcore\Model\DataObject) {
  2204.                                     $currentId $phenomenaObj->getphenomenaListId();
  2205.                                     $existingIds array_column($response'id');
  2206.                                     if (!in_array($currentId$existingIds)) {
  2207.                                         $response[] = [
  2208.                                             "id" => $currentId,
  2209.                                             "nameEn" => $phenomenaObj->getTitle("en"),
  2210.                                             "nameAr" => $phenomenaObj->getTitle("ar"),
  2211.                                             "criteria" => $phenomena->getCriteria()
  2212.                                         ];
  2213.                                     } else {
  2214.                                         $index array_search($currentId$existingIds);
  2215.                                         $response[$index]['criteria'] .= ', ' $phenomena->getCriteria();
  2216.                                     }
  2217.                                 }
  2218.                             }
  2219.                         }
  2220.                     } else {
  2221.                         return ["success" => false"message" => "No criteria is set"];
  2222.                     }
  2223.                 }
  2224.             }
  2225.             return ["success" => true"data" => $response];
  2226.         }
  2227.         return ["success" => false"message" => "Invalid alert type id"];
  2228.     }
  2229.     public function getAlertActions()
  2230.     {
  2231.         $response = [];
  2232.         $alertActions = new DataObject\AlertAction\Listing();
  2233.         $alertActions $alertActions->load();
  2234.         if ($alertActions) {
  2235.             foreach ($alertActions as $alertAction) {
  2236.                 $response[] = [
  2237.                     "id" => $alertAction->getAlertActionId(),
  2238.                     "nameEn" => $alertAction->getName('en'),
  2239.                     "nameAr" => $alertAction->getName('ar'),
  2240.                     "severity" => $alertAction->getSeverity()
  2241.                 ];
  2242.             }
  2243.         }
  2244.         return ["success" => true"data" => $response];
  2245.     }
  2246.     public function getAlertHazards()
  2247.     {
  2248.         $response = [];
  2249.         $alertHazards = new DataObject\AlertHazard\Listing();
  2250.         $alertHazards $alertHazards->load();
  2251.         if ($alertHazards) {
  2252.             foreach ($alertHazards as $alertHazard) {
  2253.                 $response[] = [
  2254.                     "id" => $alertHazard->getalertHazardId(),
  2255.                     "nameEn" => $alertHazard->getName('en'),
  2256.                     "nameAr" => $alertHazard->getName('ar'),
  2257.                     "OrderId" => $alertHazard->getOrderId()
  2258.                 ];
  2259.             }
  2260.         }
  2261.         return ["success" => true"data" => $response];
  2262.     }
  2263.     public function getAlertTypes()
  2264.     {
  2265.         $response = [];
  2266.         $alertTypes = new DataObject\AlertType\Listing();
  2267.         $alertTypes $alertTypes->load();
  2268.         if ($alertTypes) {
  2269.             foreach ($alertTypes as $alertType) {
  2270.                 $response[] = [
  2271.                     "id" => $alertType->getAlertTypeId(),
  2272.                     "color" => $alertType->getColor(),
  2273.                     "nameEn" => $alertType->getName('en'),
  2274.                     "nameAr" => $alertType->getName('ar')
  2275.                 ];
  2276.             }
  2277.         }
  2278.         return ["success" => true"data" => $response];
  2279.     }
  2280.     public function getEvents()
  2281.     {
  2282.         $response = [];
  2283.         $events = new DataObject\Event\Listing();
  2284.         $events $events->load();
  2285.         if ($events) {
  2286.             foreach ($events as $event) {
  2287.                 $response[] = [
  2288.                     "id" => $event->getEventId(),
  2289.                     "nameEn" => $event->getName('en'),
  2290.                     "nameAr" => $event->getName('ar')
  2291.                 ];
  2292.             }
  2293.         }
  2294.         return ["success" => true"data" => $response];
  2295.     }
  2296.     public function getWeatherPhenomenones()
  2297.     {
  2298.         $response = [];
  2299.         $weatherPhenomenons = new DataObject\PhenomenaList\Listing();
  2300.         $weatherPhenomenons $weatherPhenomenons->load();
  2301.         if ($weatherPhenomenons) {
  2302.             foreach ($weatherPhenomenons as $weatherPhenomenon) {
  2303.                 $response[] = [
  2304.                     "id" => $weatherPhenomenon->getPhenomenaListId(),
  2305.                     "nameEn" => $weatherPhenomenon->getTitle('en'),
  2306.                     "nameAr" => $weatherPhenomenon->getTitle('ar')
  2307.                 ];
  2308.             }
  2309.         }
  2310.         return ["success" => true"data" => $response];
  2311.     }
  2312.     public function getWeatherPhenomenonAffect()
  2313.     {
  2314.         $response = [];
  2315.         $getWeatherPhenomAffects = new DataObject\WeatherPhenomenonAffect\Listing();
  2316.         $getWeatherPhenomAffects $getWeatherPhenomAffects->load();
  2317.         if ($getWeatherPhenomAffects) {
  2318.             foreach ($getWeatherPhenomAffects as $getWeatherPhenomAffect) {
  2319.                 $response[] = [
  2320.                     "id" => $getWeatherPhenomAffect->getWeatherPhenomenonAffectId(),
  2321.                     "nameEn" => $getWeatherPhenomAffect->getName('en'),
  2322.                     "nameAr" => $getWeatherPhenomAffect->getName('ar')
  2323.                 ];
  2324.             }
  2325.         }
  2326.         return ["success" => true"data" => $response];
  2327.     }
  2328.     public function getAlertStatuses($params)
  2329.     {
  2330.         $response = [];
  2331.         $alertStatuss = new DataObject\AlertStatus\Listing();
  2332.         // Handle search by name
  2333.         if (isset($params['search']) && !empty($params['search'])) {
  2334.             $alertStatuss->addConditionParam("name LIKE ?""%" $params['search'] . "%");
  2335.         }
  2336.         // Handle alert type filter
  2337.         if (isset($params['alert_id']) && !empty($params['alert_id'])) {
  2338.             $alertTypeIds = [];
  2339.             $alertTypeList = new DataObject\AlertType\Listing();
  2340.             $alertTypeList->addConditionParam("alertTypeId IN (?)", [$params['alert_id']]);
  2341.             foreach ($alertTypeList as $alertType) {
  2342.                 $alertTypeIds[] = $alertType->getId();
  2343.             }
  2344.             $alertStatuss->addConditionParam("alertType__id IN (?)", [$alertTypeIds]);
  2345.         }
  2346.         // Check for regionId array and isLandLocked logic
  2347.         $excludeWavesRising false;
  2348.         if (isset($params['regionId']) && is_array($params['regionId']) && count($params['regionId']) > 0) {
  2349.             foreach ($params['regionId'] as $regionId) {
  2350.                 $region \Pimcore\Model\DataObject\Region::getByRegionId($regionIdtrue);
  2351.                 if ($region && $region->getIsLandLocked()) {
  2352.                     $excludeWavesRising true;
  2353.                     break;
  2354.                 }
  2355.             }
  2356.         }
  2357.         $alertStatuss $alertStatuss->load();
  2358.         if ($alertStatuss) {
  2359.             foreach ($alertStatuss as $alertStatus) {
  2360.                 // Exclude "Waves rising" if needed
  2361.                 if ($excludeWavesRising && ((strtolower(trim($alertStatus->getName('en'))) === 'waves rising'))) {
  2362.                     continue;
  2363.                 }
  2364.                 $alertType $alertStatus->getAlertType();
  2365.                 $alertData = [];
  2366.                 if (!empty($alertType)) {
  2367.                     $alertData = [
  2368.                         "id" => $alertType->getAlertTypeId(),
  2369.                         "nameEn" => $alertType->getName('en'),
  2370.                         "nameAr" => $alertType->getName('ar')
  2371.                     ];
  2372.                 }
  2373.                 $response[] = [
  2374.                     "id" => $alertStatus->getalertStatusId(),
  2375.                     "nameEn" => $alertStatus->getName('en'),
  2376.                     "nameAr" => $alertStatus->getName('ar'),
  2377.                     "imageTemplate" => $alertStatus->getimageTemplate(),
  2378.                     "alertType" => $alertData
  2379.                 ];
  2380.             }
  2381.         }
  2382.         return ["success" => true"data" => $response];
  2383.     }
  2384.     public function getGovernorates()
  2385.     {
  2386.         $response = [];
  2387.         $governorates = new DataObject\Governorate\Listing();
  2388.         $Governorates $governorates->load();
  2389.         if ($governorates) {
  2390.             foreach ($governorates as $governorate) {
  2391.                 $region $governorate->getregionId();
  2392.                 $regionData = [];
  2393.                 if (!empty($region)) {
  2394.                     $regionData = [
  2395.                         "id" => $region->getRegionId(),
  2396.                         "nameEn" => $region->getName('en'),
  2397.                         "nameAr" => $region->getName('ar'),
  2398.                         "longitude" => $region->getLongitude(),
  2399.                         "latitude" => $region->getLongitude()
  2400.                     ];
  2401.                 }
  2402.                 $response[] = [
  2403.                     "id" => $governorate->getgovernoteId(),
  2404.                     "nameEn" => $governorate->getName('en'),
  2405.                     "nameAr" => $governorate->getName('ar'),
  2406.                     "regionId" => $regionData,
  2407.                     "longitude" => $governorate->getLongitude(),
  2408.                     "latitude" => $governorate->getLatitude(),
  2409.                     "isHidden" => $governorate->getisHidden(),
  2410.                     "IsMunicipality" => $governorate->getIsMunicipality(),
  2411.                     "MunicipalityID" => $governorate->getMunicipalityID()
  2412.                 ];
  2413.             }
  2414.         }
  2415.         return ["success" => true"data" => $response];
  2416.     }
  2417.     public function getMunicipality($governorateId null$lang 'en')
  2418.     {
  2419.         $response = [];
  2420.         $municipalities = new DataObject\Municipality\Listing();
  2421.         if (is_array($governorateId)) {
  2422.             $govIdsArr = [];
  2423.             foreach ($governorateId as $govId) {
  2424.                 $governorate \Pimcore\Model\DataObject\Governorate::getByGovernoteId($govIdtrue);
  2425.                 if ($governorate) {
  2426.                     array_push($govIdsArr$governorate->getId());
  2427.                 }
  2428.             }
  2429.             $municipalities->setCondition("governorate__id IN (" implode(", "$govIdsArr) . ")");
  2430.         } else {
  2431.             if ($governorateId) {
  2432.                 $governorate \Pimcore\Model\DataObject\Governorate::getByGovernoteId($governorateIdtrue);
  2433.                 $municipalities->setCondition("governorate__id = ?", [$governorate->getId()]);
  2434.             }
  2435.         }
  2436.         $municipalities $municipalities->load();
  2437.         if ($municipalities) {
  2438.             foreach ($municipalities as $municipality) {
  2439.                 $response[] = [
  2440.                     "id" => $municipality->getMunicipalityid(),
  2441.                     "nameEn" => $municipality->getName('en'),
  2442.                     "nameAr" => $municipality->getName('ar'),
  2443.                     "longitude" => $municipality->getLongitude(),
  2444.                     "latitude" => $municipality->getLatitude(),
  2445.                     "governate" => $municipality->getGovernorate()->getGovernoteId()
  2446.                 ];
  2447.             }
  2448.         }
  2449.         // Determine sorting field based on language
  2450.         $sortField 'nameEn'// Default sorting by English
  2451.         if (isset($lang) && strtolower($lang) === 'ar') {
  2452.             $sortField 'nameAr'// Sorting by Arabic
  2453.         }
  2454.         // Sort manually using usort()
  2455.         usort($response, function ($a$b) use ($sortField) {
  2456.             return strcmp($a[$sortField], $b[$sortField]);
  2457.         });
  2458.         return ["success" => true"data" => $response];
  2459.     }
  2460.     public function getMunicipalityByParams($governorateId null,$search null$lang 'en')
  2461.     {
  2462.         $response = [];
  2463.         $municipalities = new DataObject\Municipality\Listing();
  2464.         if (is_array($governorateId)) {
  2465.             $govIdsArr = [];
  2466.             foreach ($governorateId as $govId) {
  2467.                 $governorate \Pimcore\Model\DataObject\Governorate::getByGovernoteId($govIdtrue);
  2468.                 if ($governorate) {
  2469.                     array_push($govIdsArr$governorate->getId());
  2470.                 }
  2471.             }
  2472.             $municipalities->setCondition("governorate__id IN (" implode(", "$govIdsArr) . ")");
  2473.         } else {
  2474.             if ($governorateId) {
  2475.                 $governorate \Pimcore\Model\DataObject\Governorate::getByGovernoteId($governorateIdtrue);
  2476.                 $municipalities->setCondition("governorate__id = ?", [$governorate->getId()]);
  2477.             }
  2478.         }
  2479.         if (isset($search)) {
  2480.             $municipalities->addConditionParam("name LIKE ?""%{$search}%");
  2481.         }
  2482.         $municipalities $municipalities->load();
  2483.         if ($municipalities) {
  2484.             foreach ($municipalities as $municipality) {
  2485.                 $response[] = [
  2486.                     "id" => $municipality->getMunicipalityid(),
  2487.                     "nameEn" => $municipality->getName('en'),
  2488.                     "nameAr" => $municipality->getName('ar'),
  2489.                     "longitude" => $municipality->getLongitude(),
  2490.                     "latitude" => $municipality->getLatitude(),
  2491.                     "governate" => $municipality->getGovernorate()->getGovernoteId()
  2492.                 ];
  2493.             }
  2494.         }
  2495.         // Determine sorting field based on language
  2496.         $sortField 'nameEn'// Default sorting by English
  2497.         if (isset($lang) && strtolower($lang) === 'ar') {
  2498.             $sortField 'nameAr'// Sorting by Arabic
  2499.         }
  2500.         // Sort manually using usort()
  2501.         usort($response, function ($a$b) use ($sortField) {
  2502.             return strcmp($a[$sortField], $b[$sortField]);
  2503.         });
  2504.         return ["success" => true"data" => $response];
  2505.     }
  2506.     public function getGovernoratesByRegion($params)
  2507.     {
  2508.         $response = [];
  2509.         $governorates = new DataObject\Governorate\Listing();
  2510.         if (isset($params['region_id'])) {
  2511.             $region \Pimcore\Model\DataObject\Region::getByRegionId($params['region_id'], true);
  2512.             if (!$region) {
  2513.                 throw new \Exception("Region not available");
  2514.             }
  2515.             $governorates->filterByRegionId($region);
  2516.         }
  2517.         if (isset($params['region_ids']) && !empty($params['region_ids'])) {
  2518.             $regionIds = [];
  2519.             $regionList = new DataObject\Region\Listing();
  2520.             $regionList->addConditionParam("regionId IN (?)", [$params['region_ids']]);
  2521.             foreach ($regionList as $region) {
  2522.                 $regionIds[] = $region->getId();
  2523.             }
  2524.             $governorates->addConditionParam("regionId__id IN (?)", [$regionIds]);
  2525.         }
  2526.         // Load governorates without sorting in Pimcore
  2527.         $governorates $governorates->load();
  2528.         // Convert to array for manual sorting
  2529.         $count 0;
  2530.         if ($governorates) {
  2531.             foreach ($governorates as $governorate) {
  2532.                 $region $governorate->getregionId();
  2533.                 $regionData = [];
  2534.                 if (!empty($region)) {
  2535.                     $regionData = [
  2536.                         "id" => $region->getRegionId(),
  2537.                         "nameEn" => $region->getName('en'),
  2538.                         "nameAr" => $region->getName('ar'),
  2539.                         "longitude" => $region->getLongitude(),
  2540.                         "latitude" => $region->getLatitude()
  2541.                     ];
  2542.                 }
  2543.                 $response[$count] = [
  2544.                     "id" => $governorate->getgovernoteId(),
  2545.                     "nameEn" => $governorate->getName('en'),
  2546.                     "nameAr" => $governorate->getName('ar'),
  2547.                     "regionId" => $regionData,
  2548.                     "longitude" => $governorate->getLongitude(),
  2549.                     "latitude" => $governorate->getLatitude(),
  2550.                     "isHidden" => $governorate->getisHidden(),
  2551.                     "IsMunicipality" => $governorate->getIsMunicipality(),
  2552.                     "MunicipalityID" => $governorate->getMunicipalityID(),
  2553.                     "bbox" => $governorate->getBbox()
  2554.                 ];
  2555.                 if ($governorate->getMunicipalityID() && $governorate->getIsMunicipality()) {
  2556.                     $municipality \Pimcore\Model\DataObject\Municipality::getBymunicipalityid($governorate->getMunicipalityID(), true);
  2557.                     if ($municipality) {
  2558.                         $muniGovernate $municipality->getgovernorate();
  2559.                         if ($muniGovernate) {
  2560.                             $response[$count]["parent_id"] = $muniGovernate->getgovernoteId();
  2561.                             $response[$count]["parent_longitude"] = $muniGovernate->getLongitude();
  2562.                             $response[$count]["parent_latitude"] = $muniGovernate->getLatitude();
  2563.                             $response[$count]["parent_name_en"] = $muniGovernate->getName("en");
  2564.                             $response[$count]["parent_name_ar"] = $muniGovernate->getName("ar");
  2565.                         }
  2566.                     }
  2567.                 }
  2568.                 $count++;
  2569.             }
  2570.         }
  2571.         // Determine sorting field based on language
  2572.         $sortField 'nameEn'// Default sorting by English
  2573.         if (isset($params['lang']) && strtolower($params['lang']) === 'ar') {
  2574.             $sortField 'nameAr'// Sorting by Arabic
  2575.         }
  2576.         // Sort manually using usort()
  2577.         usort($response, function ($a$b) use ($sortField) {
  2578.             return strcmp($a[$sortField], $b[$sortField]);
  2579.         });
  2580.         return ["success" => true"data" => $response];
  2581.     }
  2582.     public function getGovernoratesByParams($params)
  2583.     {
  2584.         $response = [];
  2585.         $governorates = new DataObject\Governorate\Listing();
  2586.         if (isset($params['region_id'])) {
  2587.             $region \Pimcore\Model\DataObject\Region::getByRegionId($params['region_id'], true);
  2588.             if (!$region) {
  2589.                 throw new \Exception("Region not available");
  2590.             }
  2591.             $governorates->filterByRegionId($region);
  2592.         }
  2593.         if (isset($params['region_ids']) && !empty($params['region_ids'])) {
  2594.             $regionIds = [];
  2595.             $regionList = new DataObject\Region\Listing();
  2596.             $regionList->addConditionParam("regionId IN (?)", [$params['region_ids']]);
  2597.             foreach ($regionList as $region) {
  2598.                 $regionIds[] = $region->getId();
  2599.             }
  2600.             $governorates->addConditionParam("regionId__id IN (?)", [$regionIds]);
  2601.         }
  2602.         if (isset($params['search'])) {
  2603.             $governorates->addConditionParam("name LIKE ?""%{$params['search']}%");
  2604.         }
  2605.         // Load governorates without sorting in Pimcore
  2606.         $governorates $governorates->load();
  2607.         // Convert to array for manual sorting
  2608.         $count 0;
  2609.         if ($governorates) {
  2610.             foreach ($governorates as $governorate) {
  2611.                 $region $governorate->getregionId();
  2612.                 $regionData = [];
  2613.                 if (!empty($region)) {
  2614.                     $regionData = [
  2615.                         "id" => $region->getRegionId(),
  2616.                         "nameEn" => $region->getName('en'),
  2617.                         "nameAr" => $region->getName('ar'),
  2618.                         "longitude" => $region->getLongitude(),
  2619.                         "latitude" => $region->getLatitude()
  2620.                     ];
  2621.                 }
  2622.                 $response[$count] = [
  2623.                     "id" => $governorate->getgovernoteId(),
  2624.                     "nameEn" => $governorate->getName('en'),
  2625.                     "nameAr" => $governorate->getName('ar'),
  2626.                     "regionId" => $regionData,
  2627.                     "longitude" => $governorate->getLongitude(),
  2628.                     "latitude" => $governorate->getLatitude(),
  2629.                     "isHidden" => $governorate->getisHidden(),
  2630.                     "IsMunicipality" => $governorate->getIsMunicipality(),
  2631.                     "MunicipalityID" => $governorate->getMunicipalityID(),
  2632.                     "bbox" => $governorate->getBbox()
  2633.                 ];
  2634.                 if ($governorate->getMunicipalityID() && $governorate->getIsMunicipality()) {
  2635.                     $municipality \Pimcore\Model\DataObject\Municipality::getBymunicipalityid($governorate->getMunicipalityID(), true);
  2636.                     if ($municipality) {
  2637.                         $muniGovernate $municipality->getgovernorate();
  2638.                         if ($muniGovernate) {
  2639.                             $response[$count]["parent_id"] = $muniGovernate->getgovernoteId();
  2640.                             $response[$count]["parent_longitude"] = $muniGovernate->getLongitude();
  2641.                             $response[$count]["parent_latitude"] = $muniGovernate->getLatitude();
  2642.                             $response[$count]["parent_name_en"] = $muniGovernate->getName("en");
  2643.                             $response[$count]["parent_name_ar"] = $muniGovernate->getName("ar");
  2644.                         }
  2645.                     }
  2646.                 }
  2647.                 $count++;
  2648.             }
  2649.         }
  2650.         // Determine sorting field based on language
  2651.         $sortField 'nameEn'// Default sorting by English
  2652.         if (isset($params['lang']) && strtolower($params['lang']) === 'ar') {
  2653.             $sortField 'nameAr'// Sorting by Arabic
  2654.         }
  2655.         // Sort manually using usort()
  2656.         usort($response, function ($a$b) use ($sortField) {
  2657.             return strcmp($a[$sortField], $b[$sortField]);
  2658.         });
  2659.         return ["success" => true"data" => $response];
  2660.     }
  2661.     public function getVersions($notificationObject$translator)
  2662.     {
  2663.         $result = [];
  2664.         $versions $notificationObject->getVersions();
  2665.         if ($versions) {
  2666.             foreach ($versions as $version) {
  2667.                 $note $version->getNote();
  2668.                 if ($note == "Update") {
  2669.                     $viewNotification $version->loadData();
  2670.                     if ($viewNotification instanceof EwsNotification) {
  2671.                         $result[] = $this->createNotificationFormat($viewNotification$translator);
  2672.                     }
  2673.                 }
  2674.             }
  2675.         }
  2676.         return $result;
  2677.     }
  2678.     private function createNotificationFormat($notification$translator)
  2679.     {
  2680.         $response = [];
  2681.         $critetia '';
  2682.         $critetiaAr '';
  2683.         $status 'active';
  2684.         $alertType $notification->getAlertType();
  2685.         $phenomena $notification->getWeatherPhenomenon();
  2686.         // Set the timezone to Asia/Riyadh
  2687.         $timezone = new \DateTimeZone(TIMEZONE);
  2688.         // Convert start and end dates to Asia/Riyadh timezone
  2689.         $startDate $notification->getStartDate() ? $notification->getStartDate()->setTimezone($timezone) : null;
  2690.         $endDate $notification->getEndDate() ? $notification->getEndDate()->setTimezone($timezone) : null;
  2691.         if ($alertType && $phenomena) {
  2692.             $phenomenaList $alertType->getPhenomenaList();
  2693.             if ($phenomenaList) {
  2694.                 $items $phenomenaList->getItems();
  2695.                 if ($items) {
  2696.                     foreach ($items as $item) {
  2697.                         if ($item->getPhenomena() == $phenomena->getId()) {
  2698.                             $critetia $item->getCriteria();
  2699.                             $critetiaAr $item->getCriteriaAr();
  2700.                             break;
  2701.                         }
  2702.                     }
  2703.                 }
  2704.             }
  2705.         }
  2706.         if ($endDate && $endDate < new \DateTime('now'$timezone)) {
  2707.             $status "expired";
  2708.         }
  2709.         $twentyFourHoursAgo = (new \DateTime('now'$timezone))->modify('-24 hours');
  2710.         if ($endDate && $endDate $twentyFourHoursAgo) {
  2711.             $status "archived";
  2712.         }
  2713.         $response = [
  2714.             "id" => $notification->getId(),
  2715.             "searchEwsIdEn" => $notification->getEwsSearchId("en"),
  2716.             "searchEwsIdAr" => $notification->getEwsSearchId("ar"),
  2717.             "title" => $startDate $startDate->format("dmY") . '-' $notification->getId() : '',
  2718.             "alertType" => !empty($notification->getAlertType()) ? $notification->getAlertType()->getAlertTypeId() : "",
  2719.             "alertTypeAr" => !empty($notification->getAlertType()) ? $notification->getAlertType()->getName("ar") : "",
  2720.             "alertTypeEn" => !empty($notification->getAlertType()) ? ucwords($notification->getAlertType()->getColor()) : "",
  2721.             "fromDate" => $startDate $startDate->format("Y-m-d H:i:s") : "",
  2722.             "toDate" => $endDate $endDate->format("Y-m-d H:i:s") : "",
  2723.             "alertStatusID" => !empty($notification->getAlertStatus()) ? $notification->getAlertStatus()->getAlertStatusId() : "",
  2724.             "alertStatusAr" => !empty($notification->getAlertStatus()) ? $notification->getAlertStatus()->getName("ar") : "",
  2725.             "alertStatusEn" => !empty($notification->getAlertStatus()) ? $notification->getAlertStatus()->getName("en") : "",
  2726.             "alertStatusCategory" => "",
  2727.             "alertHazard" => !empty($notification->getAlertHazard()) ? $this->getAlertHazardArr($notification->getAlertHazard()) : [],
  2728.             "regionID" => !empty($notification->getRegion()) ? $notification->getRegion()->getRegionId() : "",
  2729.             "regionAR" => !empty($notification->getRegion()) ? $notification->getRegion()->getName("ar") : "",
  2730.             "regionEn" => !empty($notification->getRegion()) ? $notification->getRegion()->getName("en") : "",
  2731.             "governorates" => ($notification->getRegion()) ? $this->getGovernorateDetail($notification->getGovernorate()) : [],
  2732.             "ewsOtherLocations" => ($notification->getRegion()) ? $this->getGovernorateDetail($notification->getEwsOtherLocations()) : [],
  2733.             "otherLocationsAr" => $this->getOtherLocationsNames($notification->getEwsOtherLocations(), 'ar'),
  2734.             "otherLocationsEn" => $this->getOtherLocationsNames($notification->getEwsOtherLocations(), 'en'),
  2735.             "tweetID" => "",
  2736.             "enableTwitterNotification" => $notification->getEnableTwitterNotification(),
  2737.             "enableSMSNotification" => $notification->getEnableSMSNotification(),
  2738.             "enableEmailNotification" => $notification->getEnableEmailNotification(),
  2739.             "alertActions" => $this->getAlertActionsByArr($notification->getAlertAction()),
  2740.             "municipalities" => $this->getMunicipalityArr($notification->getMunicipality()),
  2741.             "lastModified" => ($notification->getModificationDate()) ? date("Y-m-d H:i:s"$notification->getModificationDate()) : "",
  2742.             "last_modified_date" => ($notification->getModificationDate()) ? date("Y-m-d"$notification->getModificationDate()) : "",
  2743.             'coordinates' => $notification->getCoordinates(),
  2744.             'message' => $notification->getMessage("en"),
  2745.             "file" => (!empty($notification->getAttachment())) ? API_BASE_URL $notification->getAttachment()->getFullPath() : [],
  2746.             'criteria' => $critetia,
  2747.             'criteriaAr' => $critetiaAr,
  2748.             'created_at' => ($notification->getCreationDate()) ? date("Y-m-d H:i:s"$notification->getCreationDate()) : "",
  2749.             'created_by' => ($notification->getUser()) ? $notification->getUser()->getName() : "",
  2750.             'edited_by' => ($notification->getEditor()) ? $notification->getEditor()->getName() : (($notification->getUser()) ? $notification->getUser()->getName() : ""),
  2751.             'status_en' => ucfirst($status),
  2752.             'status_ar' => $translator->trans(ucfirst($status), [], null'ar'),
  2753.             'previewText' => $notification->getPreviewText() ?? false,
  2754.         ];
  2755.         return $response;
  2756.     }
  2757.     private function getAlertHazardArr($alertHazardArr)
  2758.     {
  2759.         $result = [];
  2760.         if ($alertHazardArr) {
  2761.             foreach ($alertHazardArr as $affect) {
  2762.                 $result[] = [
  2763.                     "pim_id" => $affect->getId(),
  2764.                     "id" => $affect->getAlertHazardId(),
  2765.                     "nameEn" => $affect->getName("en"),
  2766.                     "nameAr" => $affect->getName("ar"),
  2767.                 ];
  2768.             }
  2769.         }
  2770.         return $result;
  2771.     }
  2772.     private function getAlertActionsByArr($alertActions)
  2773.     {
  2774.         $result = [];
  2775.         if ($alertActions) {
  2776.             foreach ($alertActions as $alert) {
  2777.                 $result[] = [
  2778.                     "pim_id" => $alert->getId(),
  2779.                     "id" => $alert->getAlertActionId(),
  2780.                     "descriptionEn" => $alert->getName("en"),
  2781.                     "descriptionAr" => $alert->getName("ar"),
  2782.                 ];
  2783.             }
  2784.         }
  2785.         return $result;
  2786.     }
  2787.     private function getMunicipalityArr($municipalities)
  2788.     {
  2789.         $result = [];
  2790.         if ($municipalities) {
  2791.             foreach ($municipalities as $municipality) {
  2792.                 // p_R($municipality->getName());                
  2793.                 $result[] = [
  2794.                     "id" => $municipality->getMunicipalityId(),
  2795.                     "nameEn" => $municipality->getName("en"),
  2796.                     "nameAr" => $municipality->getName("ar"),
  2797.                     "governate" => $municipality->getGovernorate()->getGovernoteId()
  2798.                 ];
  2799.             }
  2800.         }
  2801.         return $result;
  2802.     }
  2803.     private function getOtherLocationsNames($otherLocations$lang 'ar')
  2804.     {
  2805.         $names = []; // Step 1: Initialize an array to hold the names
  2806.         if ($otherLocations) {
  2807.             foreach ($otherLocations as $otherLocation) {
  2808.                 $names[] = $otherLocation->getName($lang); // Step 2 & 3: Extract and collect names
  2809.             }
  2810.         }
  2811.         if (empty($names)) { // Check if the names array is empty
  2812.             return ''// Return an empty string if there are no names
  2813.         }
  2814.         $namesString implode(', '$names); // Step 4: Convert the array to a comma-separated string
  2815.         return $namesString// Return or use the comma-separated string as needed
  2816.     }
  2817.     public function publishedEwsNotification($notificationId$published$userGroupIds$translator$emailService$templating$logger)
  2818.     {
  2819.         $result = [];
  2820.         $viewNotification DataObject\EwsNotification::getById($notificationIdfalse);
  2821.         if (!$viewNotification) {
  2822.             $viewNotification DataObject\EwsNotification::getById($notificationIdtrue);
  2823.         }
  2824.         if ($published) {
  2825.             $viewNotification->setStatus("active");
  2826.         } else {
  2827.             $viewNotification->setStatus("");
  2828.         }
  2829.         $viewNotification->setPublished($published);
  2830.         //set ews search Id
  2831.         $currentDate = new \DateTime();
  2832.         $formattedDate $currentDate->format('dmY') . '-' $viewNotification->getId();
  2833.         $searchIdEn 'Early Warning System | ' $formattedDate ' | ' ucfirst($viewNotification->getAlertType()?->getColor()) . ' Alert | ' $viewNotification->getWeatherPhenomenon()?->getTitle("en");
  2834.         $searchIdAr $translator->trans('Early Warning System', [], null"ar") . ' | ' $formattedDate ' | ' $translator->trans(ucfirst($viewNotification->getAlertType()?->getColor()) . ' Alert', [], null"ar") . ' | ' $viewNotification->getWeatherPhenomenon()?->getTitle("ar");
  2835.         $viewNotification->setEwsSearchId($searchIdEn"en");
  2836.         $viewNotification->setEwsSearchId($searchIdAr"ar");
  2837.         $viewNotification->save(["versionNote" => "Update"]);
  2838.         $viewNotification->save();
  2839.         // $alert = $this->createNotificationFormat($viewNotification, $translator);
  2840.         if ($published) {
  2841.             // Ensure you use 'php' to execute the command.
  2842.             $jsonUserGroupIds json_encode($userGroupIds);
  2843.             $process = new Process(['php''bin/console''app:send-early-warning-alert-email''--alertId=' $viewNotification->getId(), '--userGroupIds=' $jsonUserGroupIds]);
  2844.             $process->setWorkingDirectory(PIMCORE_PROJECT_ROOT);
  2845.             try {
  2846.                 $process->mustRun();
  2847.                 $result['success'] = true;
  2848.                 $logger->info("published EwsNotification command executed successfully: " $process->getOutput());
  2849.                 $result['message'] = $process->getOutput();
  2850.             } catch (ProcessFailedException $exception) {
  2851.                 $logger->error("published EwsNotification command failed: " $exception->getMessage());
  2852.                 return ['success' => false'message' => $exception->getMessage()];
  2853.             }
  2854.         }
  2855.         return ["success" => true"message" => $translator->trans("ews_notification_published")];
  2856.     }
  2857.     public function reportEwsNotification($param$translator)
  2858.     {
  2859.         $result = [];
  2860.         $region null;
  2861.         try {
  2862.             $listing = new EwsNotification\Listing();
  2863.             $reportType = isset($param['report_type']) ? $param['report_type'] : null;
  2864.             $regionId = isset($param['region_id']) ? $param['region_id'] : null;
  2865.             if ($regionId) {
  2866.                 $region \Pimcore\Model\DataObject\Region::getByRegionId($regionIdtrue);
  2867.                 if (!$region) {
  2868.                     throw new \Exception("Region not exists");
  2869.                 }
  2870.             }
  2871.             switch ($reportType) {
  2872.                 case 'published_in_week':
  2873.                     $listing->setCondition("o_published = true AND o_creationDate >= " strtotime("-1 week"));
  2874.                     break;
  2875.                 case 'published_in_day':
  2876.                     $listing->setCondition("o_published = true AND o_creationDate >= " strtotime("-1 day"));
  2877.                     break;
  2878.                 case 'published_in_month':
  2879.                     $listing->setCondition("o_published = true AND o_creationDate >= " strtotime("-1 month"));
  2880.                     break;
  2881.                 case 'published_in_year':
  2882.                     $listing->setCondition("o_published = true AND o_creationDate >= " strtotime("-1 year"));
  2883.                     break;
  2884.                 case 'published_in_region':
  2885.                     $listing->setCondition("o_published = true AND region__id = ?", [$region->getId()]);
  2886.                     break;
  2887.                 case 'published_in_draft':
  2888.                     $listing->setCondition("o_published = false");
  2889.                     break;
  2890.                 default:
  2891.                     throw new \Exception("Invalid request");
  2892.                     break;
  2893.             }
  2894.             $result['count'] = $listing->getCount();
  2895.             $notifications $listing->load();
  2896.             if ($notifications) {
  2897.                 foreach ($notifications as $notification) {
  2898.                     $result['data'][] = $this->createNotificationFormat($notification$translator);
  2899.                 }
  2900.             }
  2901.             return $result;
  2902.         } catch (\Exception $ex) {
  2903.             return new \Exception($ex->getMessage());
  2904.         }
  2905.         return $result;
  2906.     }
  2907.     private function sendEmailNotification($users$alert$emailService$templating)
  2908.     {
  2909.         $mailSent null;
  2910.         if ($alert) {
  2911.             $governatesArr $alert['governorates'];
  2912.             $governates = [];
  2913.             if ($governatesArr) {
  2914.                 foreach ($governatesArr as $gov) {
  2915.                     $governates[] = $gov['nameEn'];
  2916.                 }
  2917.             }
  2918.             $data $alert;
  2919.             $data['sender'] = 'National Center for Meteorology';
  2920.             //$subject = $alert['searchEwsIdAr'] ??'Severe Weather Alert - ' . $gov['nameEn'];
  2921.             // $subject = $alert['alertStatusAr'] . ' - ' . $alert['regionAR'];
  2922.             // $subject = 'النظام الالي للإنذار المبكر :' . ' - ' . $alert['searchEwsIdAr'];
  2923.             $parts explode('|'$alert['searchEwsIdAr']);
  2924.             $excluded implode('|'array_slice($parts1));
  2925.             $subject 'النظام الالي للإنذار المبكر :'  $excluded;
  2926.             // $html = $templating->render('web2print/_manned_alert_notification_ar.html.twig', $data);
  2927.             //sending an email document (pimcore document)
  2928.             // $emailAlertTemplate = '/email/alert_notification';
  2929.             if ($users) {
  2930.                 foreach ($users as $currentUser) {
  2931.                     if (isset($alert['enableEmailNotification']) && $alert['enableEmailNotification']) {
  2932.                         if ($currentUser->getSendEwsEmail() || is_null($currentUser->getSendEwsEmail())) {
  2933.                             # code...
  2934.                             // $email = $currentUser->getEmail();
  2935.                             // $param = ['user' => $currentUser, 'message' => $html, 'url' => null];
  2936.                             // $mailSent = $emailService->sendMail($param, $email, $emailAlertTemplate, $subject);
  2937.                             // $data['name'] = $user->getName();
  2938.                             // extra param js
  2939.                             $alertObj \Pimcore\Model\DataObject\AlertType::getByAlertTypeId($alert['alertType'], true);
  2940.                             $alertName $alertObj->getColor() ? $alertObj->getColor() : '';
  2941.                             $alert['user_name'] = $currentUser->getName();
  2942.                             $alert['host'] = API_BASE_URL;
  2943.                             $alert['alertColor'] = $alertName;
  2944.                             $backgroundColor '#fcb82526';
  2945.                             $borderColor '#000000';
  2946.                             $textColor '#000000';
  2947.                             // unsubscribe ews notificaiton token
  2948.                             $tokenURL '';
  2949.                             $token $this->userModel->unSubscribeEwsGenerateToken($currentUser->getEmail());
  2950.                             if (!empty($token)) {
  2951.                                 $tokenURL BASE_URL '/unsubscribe/notification?ews=true&token=' $token;
  2952.                             }
  2953.                             if (isset($alert['alertColor']) && !empty(trim($alert['alertColor']))) {
  2954.                                 $alertColorLower strtolower(trim($alert['alertColor']));
  2955.                                 if ($alertColorLower === 'red') {
  2956.                                     $backgroundColor '#f6000017';
  2957.                                     $borderColor '#F60000';
  2958.                                     $textColor '#F60000';
  2959.                                 } elseif ($alertColorLower === 'orange') {
  2960.                                     $backgroundColor '#ff66001f';
  2961.                                     $borderColor '#FF6600';
  2962.                                     $textColor '#FF6600';
  2963.                                 } elseif ($alertColorLower === 'yellow') {
  2964.                                     $backgroundColor '#fcb82526';
  2965.                                     $borderColor '#FCB825';
  2966.                                     $textColor '#FCB825';
  2967.                                 }
  2968.                             }
  2969.                             $purpose EWS_MESSAGE;
  2970.                             $alert['backgroundColor'] = $backgroundColor;
  2971.                             $alert['borderColor'] = $borderColor;
  2972.                             $alert['textColor'] = $textColor;
  2973.                             $alert['tokenURL'] = $tokenURL;
  2974.                             $alert['mannedAlertDatailUrl'] = null;
  2975.                             $html $templating->render('web2print/_manned_alert_notification_ar.html.twig'$alert);
  2976.                             $mailSent $this->c2Service->sendNotificationEmail($_ENV['EWS_MAIL_TEMPLATE'], $alert['id'], $currentUser->getId(), $html$subject$purpose);
  2977.                             // $this->c2Service->sendMannedAlertEmails($html,$currentUser->getId(),$alert['id']);
  2978.                             // $mailSent=$this->c2Service->sendMannedAlertEmails($html,$currentUser->getId(),$alert['id'],$subject);
  2979.                             if ($mailSent) {
  2980.                                 $status "sent";
  2981.                                 $this->saveEmailStatus($currentUser->getEmail(), $currentUser->getName(), $alert$status);
  2982.                             } else {
  2983.                                 $status "not sent";
  2984.                                 $this->saveEmailStatus($currentUser->getEmail(), $currentUser->getName(), $alert$status);
  2985.                             }
  2986.                         }
  2987.                     }
  2988.                 }
  2989.             }
  2990.         }
  2991.         return $mailSent;
  2992.     }
  2993.     private function sendExcelEmailNotification($userGroupIds$alert$emailService$templating)
  2994.     {
  2995.         $mailSent null;
  2996.         if ($alert) {
  2997.             $governatesArr $alert['governorates'];
  2998.             $governates = [];
  2999.             if ($governatesArr) {
  3000.                 foreach ($governatesArr as $gov) {
  3001.                     $governates[] = $gov['nameEn'];
  3002.                 }
  3003.             }
  3004.             $data $alert;
  3005.             $data['sender'] = 'National Center for Meteorology';
  3006.             // $subject = $alert['searchEwsIdAr'] ??'Severe Weather Alert - ' . $gov['nameEn'];
  3007.             //$subject = $alert['alertStatusAr'] . ' - ' . $alert['regionAR'];
  3008.             // $subject = 'النظام الالي للإنذار المبكر :' . ' - ' . $alert['searchEwsIdAr'];
  3009.             $parts explode('|'$alert['searchEwsIdAr']);
  3010.             $excluded implode('|'array_slice($parts1));
  3011.             $subject 'النظام الالي للإنذار المبكر :'  $excluded;
  3012.             if ($userGroupIds) {
  3013.                 foreach ($userGroupIds as $userGroupId) {
  3014.                     if ($userGroupId) {
  3015.                         $userGroup \Pimcore\Model\DataObject\EwsAndReportUserGroup::getById($userGroupIdtrue);
  3016.                         if ($userGroup instanceof \Pimcore\Model\DataObject\EwsAndReportUserGroup) {
  3017.                             foreach (json_decode($userGroup->getJsonData()) as $currentUser) {
  3018.                                 if (isset($currentUser->firstName) && isset($currentUser->lastName) && isset($currentUser->email)) {
  3019.                                     // extra param js
  3020.                                     $alertObj \Pimcore\Model\DataObject\AlertType::getByAlertTypeId($alert['alertType'], true);
  3021.                                     $alertName $alertObj->getColor() ? $alertObj->getColor() : '';
  3022.                                     $alert['user_name'] = $currentUser->firstName ' ' $currentUser->lastName;
  3023.                                     $alert['host'] = API_BASE_URL;
  3024.                                     $alert['alertColor'] = $alertName;
  3025.                                     $backgroundColor '#fcb82526';
  3026.                                     $borderColor '#000000';
  3027.                                     $textColor '#000000';
  3028.                                     if (isset($alert['alertColor']) && !empty(trim($alert['alertColor']))) {
  3029.                                         $alertColorLower strtolower(trim($alert['alertColor']));
  3030.                                         if ($alertColorLower === 'red') {
  3031.                                             $backgroundColor '#f6000017';
  3032.                                             $borderColor '#F60000';
  3033.                                             $textColor '#F60000';
  3034.                                         } elseif ($alertColorLower === 'orange') {
  3035.                                             $backgroundColor '#ff66001f';
  3036.                                             $borderColor '#FF6600';
  3037.                                             $textColor '#FF6600';
  3038.                                         } elseif ($alertColorLower === 'yellow') {
  3039.                                             $backgroundColor '#fcb82526';
  3040.                                             $borderColor '#FCB825';
  3041.                                             $textColor '#FCB825';
  3042.                                         }
  3043.                                     }
  3044.                                     $alert['backgroundColor'] = $backgroundColor;
  3045.                                     $alert['borderColor'] = $borderColor;
  3046.                                     $alert['textColor'] = $textColor;
  3047.                                     $alert['tokenURL'] = null;
  3048.                                     $alert['mannedAlertDatailUrl'] = null;
  3049.                                     $html $templating->render('web2print/_manned_alert_notification_ar.html.twig'$alert);
  3050.                                     // $email = $currentUser->email;
  3051.                                     // $param = ['user' => $currentUser, 'message' => $html, 'url' => null];
  3052.                                     // $mailSent = $emailService->sendMail($param, "abdul.muqeet@centric.ae", '/email/alert_notification', $subject);
  3053.                                     $purpose EWS_MESSAGE;
  3054.                                     $mailSent $this->c2Service->sendDefaultEmail($_ENV['EWS_MAIL_TEMPLATE'], $alert['id'], $currentUser->email$html$subject$purpose);
  3055.                                     if ($mailSent) {
  3056.                                         $status "sent";
  3057.                                         $this->saveEmailStatus($currentUser->email$currentUser->firstName ' ' $currentUser->lastName$alert$status);
  3058.                                     } else {
  3059.                                         $status "not sent";
  3060.                                         $this->saveEmailStatus($currentUser->email$currentUser->firstName ' ' $currentUser->lastName$alert$status);
  3061.                                     }
  3062.                                 }
  3063.                             }
  3064.                         }
  3065.                     }
  3066.                 }
  3067.             }
  3068.         }
  3069.         return $mailSent;
  3070.     }
  3071.     private function saveEmailStatus($userEmail$userName$alert$emailStatus)
  3072.     {
  3073.         // Get all governorates names in string seprated by ,
  3074.         $nameEnArray array_column($alert["governorates"], "nameEn");
  3075.         $nameEnString implode(", "$nameEnArray);
  3076.         $nameArArray array_column($alert["governorates"], "nameAr");
  3077.         $nameArString implode(", "$nameArArray);
  3078.         $ewsEmailStatus = new FetchSentEwsEmail();
  3079.         $ewsEmailStatus->setParent(\Pimcore\Model\DataObject\Service::createFolderByPath("/Other/FetchSentEwsEmail/"));
  3080.         $ewsEmailStatus->setKey(\Pimcore\Model\Element\Service::getValidKey($userEmail "-" strtotime("now") . "-" rand(110000), 'object'));
  3081.         $ewsEmailStatus->setEwsId($alert['id']);
  3082.         $ewsEmailStatus->setUserName($userName);
  3083.         $ewsEmailStatus->setEmail($userEmail);
  3084.         //$ewsEmailStatus->setLocationName($locationName);
  3085.         $ewsEmailStatus->setAlertType($alert['alertTypeEn'], 'en');
  3086.         $ewsEmailStatus->setAlertType($alert['alertTypeAr'], 'ar');
  3087.         $ewsEmailStatus->setAlertStatus($alert['alertStatusEn'], 'en');
  3088.         $ewsEmailStatus->setAlertStatus($alert['alertStatusAr'], 'ar');
  3089.         $ewsEmailStatus->setRegionName($alert['regionEn'], 'en');
  3090.         $ewsEmailStatus->setRegionName($alert['regionAR'], 'ar');
  3091.         $ewsEmailStatus->setGovernorateNames($nameEnString'en');
  3092.         $ewsEmailStatus->setGovernorateNames($nameArString'ar');
  3093.         $ewsEmailStatus->setStatus($emailStatus);
  3094.         $ewsEmailStatus->setPublished(true);
  3095.         $ewsEmailStatus->save();
  3096.         return $ewsEmailStatus;
  3097.     }
  3098.     public function getEwsNotificationEmailStatus($params$paginator$translator)
  3099.     {
  3100.         $result = [];
  3101.         try {
  3102.             $pageSize = isset($params['page_size']) ? $params['page_size'] : LIMIT_PER_PAGE;
  3103.             $page = isset($params['page']) ? $params['page'] : 1;
  3104.             $notificationId $params["id"] ? $params["id"] : null;
  3105.             $listing = new FetchSentEwsEmail\Listing();
  3106.             $listing->setCondition("ewsId = ? ", [$notificationId]);
  3107.             $paginator $paginator->paginate(
  3108.                 $listing,
  3109.                 $page,
  3110.                 $pageSize
  3111.             );
  3112.             if ($paginator->count() > 0) {
  3113.                 foreach ($paginator as $EwsNotificationEmail) {
  3114.                     $result[] = [
  3115.                         "ewsId" => $EwsNotificationEmail->getEwsId(),
  3116.                         "userName" => $EwsNotificationEmail->getUserName(),
  3117.                         "Email" => $EwsNotificationEmail->getEmail(),
  3118.                         "locationName" => $EwsNotificationEmail->getLocationName(),
  3119.                         "alertType" => $EwsNotificationEmail->getAlertType('en'),
  3120.                         "alertTypeAr" => $EwsNotificationEmail->getAlertType('ar'),
  3121.                         "alertStatus" => $EwsNotificationEmail->getAlertStatus('en'),
  3122.                         "alertStatusAr" => $EwsNotificationEmail->getAlertStatus('ar'),
  3123.                         "regionName" => $EwsNotificationEmail->getRegionName('en'),
  3124.                         "regionNameAr" => $EwsNotificationEmail->getRegionName('ar'),
  3125.                         "governorateNames" => explode(","$EwsNotificationEmail->getGovernorateNames('en')),
  3126.                         "governorateNamesAr" => explode(","$EwsNotificationEmail->getGovernorateNames('ar')),
  3127.                         "emailStatus" => $EwsNotificationEmail->getStatus(),
  3128.                         "emailStatusAr" => $translator->trans($EwsNotificationEmail->getStatus(), [], null"ar"),
  3129.                     ];
  3130.                 }
  3131.             }
  3132.             return ["success" => TRUE"data" => $result"paginationVariables" => $paginator->getPaginationData()];
  3133.         } catch (\Exception $ex) {
  3134.             return new \Exception($ex->getMessage());
  3135.         }
  3136.         return $result;
  3137.     }
  3138.     public function getOtherLocations()
  3139.     {
  3140.         $response = [];
  3141.         $otherLocations = new DataObject\OtherLocation\Listing();
  3142.         $otherLocations $otherLocations->load();
  3143.         if ($otherLocations) {
  3144.             foreach ($otherLocations as $otherLocation) {
  3145.                 $response[] = [
  3146.                     "id" => $otherLocation->getOtherLocationId(),
  3147.                     "nameEn" => $otherLocation->getName('en'),
  3148.                     "nameAr" => $otherLocation->getName('ar')
  3149.                 ];
  3150.             }
  3151.         }
  3152.         return ["success" => true"data" => $response];
  3153.     }
  3154.     public function getEwsNotificationByByRegionName($regionName)
  3155.     {
  3156.         $db Db::get();
  3157.         $selectedLocalities $db->fetchAll("SELECT * FROM `object_collection_AddressComponents_location` WHERE (`addressKey` = 'locality' || `addressKey` = 'administrative_area_level_1') and (TRIM(REPLACE(`addressValue`,'Province','')) = '" $regionName "')");
  3158.         return $selectedLocalities;
  3159.     }
  3160.     public function ewsAnalytics($params$connection$lang): array
  3161.     {
  3162.         $response = [];
  3163.         $status = isset($params['status']) ? $params['status'] : "";
  3164.         $isManned null;
  3165.         if (isset($params['isManned']) && $params['isManned'] == true) {
  3166.             $isManned true;
  3167.         } elseif (isset($params['isManned']) && $params['isManned'] == false) {
  3168.             $isManned false;
  3169.         }
  3170.         if (isset($params['region']) && !empty($params['region'])) {
  3171.             $region Region::getByRegionId($params['region'], true);
  3172.             $response[] = $this->ewsAnalyticsCount($region$status$connection$lang$isManned);
  3173.         } else {
  3174.             $regions = new Region\Listing();
  3175.             foreach ($regions as $region) {
  3176.                 $response[] = $this->ewsAnalyticsCount($region$status$connection$lang$isManned);
  3177.             }
  3178.         }
  3179.         return $response;
  3180.     }
  3181.     public function ewsAnalyticsCount($region$status$connection$lang$isManned)
  3182.     {
  3183.         $regionNameEn $region->getName("en");
  3184.         $regionNameAr $region->getName("ar");
  3185.         $statuses = ["active""archived""expired"];
  3186.         $count = ['regionEn' => $regionNameEn"regionAr" => $regionNameAr];
  3187.         if (in_array($status$statuses)) {
  3188.             $count[$status] = $this->getStatusData($region$status$connection$isManned);
  3189.         } else {
  3190.             foreach ($statuses as $s) {
  3191.                 $count[$s] = $this->getStatusData($region$s$connection$isManned);
  3192.             }
  3193.         }
  3194.         return $count;
  3195.     }
  3196.     public function getStatusData($region$status$connection$isManned)
  3197.     {
  3198.         $totalNotifications $this->statusCount($region$status$isManned);
  3199.         $alertTypeCount $this->alertCount($region$status$connection$isManned);
  3200.         return [
  3201.             'total_notifications' => $totalNotifications,
  3202.             'alert_type_count' => $alertTypeCount,
  3203.         ];
  3204.     }
  3205.     public function alertCount($region$status$connection$isManned)
  3206.     {
  3207.         $currentTimestamp time(); // Current UNIX timestamp
  3208.         $twentyFourHoursAgoTimestamp strtotime('-24 hours'); // 24 hours ago timestamp
  3209.         $claim_report_sql $connection->createQueryBuilder();
  3210.         $claim_report_sql
  3211.             ->select('oa.Color AS color''COUNT(oq.oo_id) AS count')
  3212.             ->from('object_ews_notification''oq')
  3213.             ->join('oq''object_query_alert_type''oa''oq.alertType__id = oa.oo_id')
  3214.             ->andWhere('oq.region__id = :regionId')
  3215.             ->andWhere('oq.status = :status');
  3216.         if ($isManned !== null && $isManned == true) {
  3217.             $claim_report_sql->andWhere('oq.isManned = 1');
  3218.         } elseif ($isManned !== null && $isManned == false) {
  3219.             $claim_report_sql->andWhere('oq.isManned = 0');
  3220.         }
  3221.         $claim_report_sql->andWhere('oq.o_published = 1'); // added new line
  3222.         // Initialize parameters array
  3223.         $params = [
  3224.             'regionId' => $region->getId(),
  3225.             'status' => $status,
  3226.         ];
  3227.         // Add conditions based on status
  3228.         if ($status == "active") {
  3229.             $claim_report_sql->andWhere('oq.endDate > :currentTimestamp');
  3230.             $params['currentTimestamp'] = $currentTimestamp;
  3231.         } else if ($status == "expired") {
  3232.             $claim_report_sql->andWhere('oq.endDate < :currentTimestamp AND oq.endDate > :twentyFourHoursAgoTimestamp');
  3233.             $params['currentTimestamp'] = $currentTimestamp;
  3234.             $params['twentyFourHoursAgoTimestamp'] = $twentyFourHoursAgoTimestamp;
  3235.         } else if ($status == "archived") {
  3236.             $claim_report_sql->andWhere('oq.endDate < :twentyFourHoursAgoTimestamp');
  3237.             $params['twentyFourHoursAgoTimestamp'] = $twentyFourHoursAgoTimestamp;
  3238.         }
  3239.         // Set all parameters at once
  3240.         $claim_report_sql->setParameters($params);
  3241.         $claim_report_sql->groupBy('color');
  3242.         $claimReportstmt $claim_report_sql->execute();
  3243.         $results $claimReportstmt->fetchAllAssociative();
  3244.         // Fetch all possible colors
  3245.         $alertTypes = new AlertType\Listing();
  3246.         $allColors = [];
  3247.         foreach ($alertTypes as $alertType) {
  3248.             if ($alertType) {
  3249.                 $allColors[] = $alertType->getColor();
  3250.             }
  3251.         }
  3252.         if (empty($allColors)) {
  3253.             $allColors = ["yellow""orange""red"];
  3254.         }
  3255.         // Create an associative array to store counts by color
  3256.         $countsByColor = [];
  3257.         foreach ($results as $result) {
  3258.             $countsByColor[$result['color']] = $result['count'];
  3259.         }
  3260.         // Merge counts for existing colors with counts for all colors, including those with count 0
  3261.         $finalResult = [];
  3262.         foreach ($allColors as $color) {
  3263.             $finalResult[] = [
  3264.                 "color" => $color,
  3265.                 "count" => isset($countsByColor[$color]) ? $countsByColor[$color] : 0
  3266.             ];
  3267.         }
  3268.         return $finalResult;
  3269.     }
  3270.     public function statusCount($region$status$isManned)
  3271.     {
  3272.         $notifications = new EwsNotification\Listing();
  3273.         $currentTimestamp time(); // Get the current UNIX timestamp
  3274.         $twentyFourHoursAgoTimestamp strtotime('-24 hours'); // 24 hours ago timestamp
  3275.         $conditions "region__id = ? AND status = ?";
  3276.         $params = [$region->getId(), $status];
  3277.         if ($status == "active") {
  3278.             $conditions .= " AND endDate > ?";
  3279.             $params[] = $currentTimestamp;
  3280.         } else if ($status == "expired") {
  3281.             $conditions .= " AND endDate < ? AND endDate > ?";
  3282.             $params[] = $currentTimestamp;
  3283.             $params[] = $twentyFourHoursAgoTimestamp;
  3284.         } else if ($status == "archived") {
  3285.             $conditions .= " AND endDate < ?";
  3286.             $params[] = $twentyFourHoursAgoTimestamp;
  3287.         }
  3288.         $notifications->addConditionParam($conditions$params);
  3289.         // Add condition for manned notifications
  3290.         if ($isManned !== null) {
  3291.             $notifications->addConditionParam("isManned = ?", [$isManned]);
  3292.         }
  3293.         return $notifications->count();
  3294.     }
  3295.     public function searchEwsNotification($params$lang$paginator$translator): array
  3296.     {
  3297.         $response = [];
  3298.         $pageSize = isset($params['page_size']) ? $params['page_size'] : LIMIT_PER_PAGE;
  3299.         $page = isset($params['page']) ? $params['page'] : 1;
  3300.         $notificationList = new DataObject\EwsNotification\Listing();
  3301.         $notificationList->setUnpublished(true);
  3302.         $notificationList->filterByPublished(true);
  3303.         if (isset($params['isManned']) && $params['isManned'] == true) {
  3304.             $notificationList->addConditionParam("isManned = 1");
  3305.         } elseif (isset($params['isManned']) && $params['isManned'] == false) {
  3306.             $notificationList->addConditionParam("isManned = 0");
  3307.         }
  3308.         if (isset($params['id']) && !empty($params['id'])) {
  3309.             $idz $params['id'];
  3310.             $notificationList->addConditionParam("oo_id IN (?)", [$idz]);
  3311.         }
  3312.         if (isset($params['alertAction']) && !empty($params['alertAction'])) {
  3313.             $alertActionIds = [];
  3314.             $alertActionList = new DataObject\AlertAction\Listing();
  3315.             $alertActionList->addConditionParam("alertActionId IN (?)", [$params['alertAction']]);
  3316.             foreach ($alertActionList as $alertAction) {
  3317.                 $alertActionIds[] = $alertAction->getId();
  3318.             }
  3319.             $alertActionids implode(","$alertActionIds);
  3320.             $notificationList->addConditionParam("alertAction REGEXP CONCAT('(^|,)(', REPLACE('$alertActionids',',', '|'), ')(,|$)')");
  3321.         }
  3322.         if (isset($params['alertType']) && !empty($params['alertType'])) {
  3323.             $alertTypeIds = [];
  3324.             $alertTypeList = new DataObject\AlertType\Listing();
  3325.             $alertTypeList->addConditionParam("alertTypeId IN (?)", [$params['alertType']]);
  3326.             foreach ($alertTypeList as $alertType) {
  3327.                 $alertTypeIds[] = $alertType->getId();
  3328.             }
  3329.             $notificationList->addConditionParam("alertType__id IN (?)", [$alertTypeIds]);
  3330.         }
  3331.         // if (isset($params['phenomena']) && !empty($params['phenomena'])) {
  3332.         //     $phenomenaIds = [];
  3333.         //     $phenomenaList = new DataObject\PhenomenaList\Listing();
  3334.         //     $phenomenaList->addConditionParam("phenomenalistid IN (?)", [$params['phenomena']]);
  3335.         //     foreach ($phenomenaList as $phenomena) {
  3336.         //         $phenomenaIds[] = $phenomena->getId();
  3337.         //     }
  3338.         //     $notificationList->addConditionParam("weatherPhenomenon__id IN (?)", [$phenomenaIds]);
  3339.         // }
  3340.         if (!empty($params['phenomena'])) {
  3341.             $alertStatusSql null;
  3342.             foreach ($params['phenomena'] as $alertStatus) {
  3343.                 $alertStatus \Pimcore\Model\DataObject\AlertStatus::getByAlertStatusId($alertStatustrue);
  3344.                 if ($alertStatus) {
  3345.                     $alertStatusSql .= "alertStatus__id = " $alertStatus->getId() . " OR ";
  3346.                 }
  3347.             }
  3348.             $notificationList->addConditionParam("(" substr($alertStatusSql0, -3) . ")");
  3349.         }
  3350.         if (isset($params['region']) && !empty($params['region'])) {
  3351.             $regionIds = [];
  3352.             $regionList = new DataObject\Region\Listing();
  3353.             $regionList->addConditionParam("regionId IN (?)", [$params['region']]);
  3354.             foreach ($regionList as $region) {
  3355.                 $regionIds[] = $region->getId();
  3356.             }
  3357.             $notificationList->addConditionParam("region__id IN (?)", [$regionIds]);
  3358.         }
  3359.         if (isset($params['governate']) && !empty($params['governate'])) {
  3360.             $governatesIds = [];
  3361.             $governateList = new DataObject\Governorate\Listing();
  3362.             $governateList->addConditionParam("governoteId IN (?)", [$params['governate']]);
  3363.             foreach ($governateList as $governate) {
  3364.                 $governatesIds[] = $governate->getId();
  3365.             }
  3366.             $governatesIds implode(","$governatesIds);
  3367.             $notificationList->addConditionParam("governorate REGEXP CONCAT('(^|,)(', REPLACE('$governatesIds',',', '|'), ')(,|$)')");
  3368.         }
  3369.         if (isset($params['status'][0]) && !empty($params['status'][0])) {
  3370.             $currentTimestamp time();
  3371.             $twentyFourHoursAgo strtotime('-24 hours');
  3372.             if ($params['status'][0] == "active" && (!isset($params['status'][1]) || empty($params['status'][1]))) {
  3373.                 // Filter notifications with an end date in the future or equal to current time
  3374.                 $notificationList->filterByEndDate($currentTimestamp">=");
  3375.             } else if ($params['status'][0] == "expired" && (!isset($params['status'][1]) || empty($params['status'][1]))) {
  3376.                 // Filter notifications with an end date in the past, but not older than 24 hours
  3377.                 $notificationList->filterByEndDate($currentTimestamp"<");
  3378.                 $notificationList->filterByEndDate($twentyFourHoursAgo">=");
  3379.             } else if ($params['status'][0] == "archived" && (!isset($params['status'][1]) || empty($params['status'][1]))) {
  3380.                 // Filter notifications with an end date older than 24 hours
  3381.                 $notificationList->filterByEndDate($twentyFourHoursAgo"<");
  3382.             } else if (isset($params['status'][1]) && !empty($params['status'][1])) {
  3383.                 if (($params['status'][0] == "active" && $params['status'][1] == "expired") || ($params['status'][0] == "expired" && $params['status'][1] == "active")) {
  3384.                     // Filter notifications that are either active or expired
  3385.                     $notificationList->filterByEndDate($currentTimestamp">=");
  3386.                     $notificationList->filterByEndDate($twentyFourHoursAgo">=");
  3387.                 }
  3388.             }
  3389.         } else {
  3390.             // If no status is provided, return 0 records
  3391.             $notificationList->addConditionParam("1 = 0");
  3392.         }
  3393.         $notificationList->setLocale($lang);
  3394.         if (isset($params['searchId']) && !empty($params['searchId'])) {
  3395.             $notificationList->addConditionParam(
  3396.                 "(ewsSearchId LIKE ?)",
  3397.                 [
  3398.                     '%' $params['searchId'] . '%'
  3399.                 ]
  3400.             );
  3401.         }
  3402.         if (isset($params['alertHazard']) && !empty($params['alertHazard'])) {
  3403.             $alertHazardIds = [];
  3404.             $alertHazardList = new DataObject\AlertHazard\Listing();
  3405.             $alertHazardList->addConditionParam("alertHazardId IN (?)", [$params['alertHazard']]);
  3406.             foreach ($alertHazardList as $alertHazard) {
  3407.                 $alertHazardIds[] = $alertHazard->getId();
  3408.             }
  3409.             $alertHazardIds implode(","$alertHazardIds);
  3410.             $notificationList->addConditionParam("alertHazard REGEXP CONCAT('(^|,)(', REPLACE('$alertHazardIds',',', '|'), ')(,|$)')");
  3411.         }
  3412.         if (isset($params['municipality']) && !empty($params['municipality'])) {
  3413.             $municipalityIds = [];
  3414.             $municipalityList = new DataObject\Municipality\Listing();
  3415.             $municipalityList->addConditionParam("municipalityid IN (?)", [$params['municipality']]);
  3416.             foreach ($municipalityList as $municipality) {
  3417.                 $municipalityIds[] = $municipality->getId();
  3418.             }
  3419.             $municipalityIds implode(","$municipalityIds);
  3420.             $notificationList->addConditionParam("municipality REGEXP CONCAT('(^|,)(', REPLACE('$municipalityIds',',', '|'), ')(,|$)')");
  3421.         }
  3422.         if (isset($params['startDate']) && !empty($params['startDate'])) {
  3423.             $notificationList->filterByStartDate(strtotime($params['startDate']), ">=");
  3424.         }
  3425.         if (isset($params['endDate']) && !empty($params['endDate'])) {
  3426.             $notificationList->filterByEndDate(strtotime($params['endDate']), "<=");
  3427.         }
  3428.         if (isset($params['fromDate']) && isset($params['toDate']) && !empty($params['fromDate']) && !empty($params['toDate'])) {
  3429.             $fromDate = new \DateTime($params['fromDate']);
  3430.             $toDate = new \DateTime($params['toDate']);
  3431.             // Ensure the dates are in the correct format
  3432.             $fromDateStr strtotime($fromDate->format('Y-m-d H:i:s'));
  3433.             $toDateStr strtotime($toDate->format('Y-m-d') . ' 23:59:59');
  3434.             $notificationList->addConditionParam(
  3435.                 "(o_creationDate >= ? AND o_creationDate <= ?)",
  3436.                 [$fromDateStr$toDateStr]
  3437.             );
  3438.         }
  3439.         $notifications $notificationList->load();
  3440.         $sortedData = [];
  3441.         foreach ($notifications as $notification) {
  3442.             $regionEn $notification->getRegion()->getName("en"); // Assuming getRegion() is available
  3443.             $startDate $notification->getStartDate()->format('Y-m-d H:i:s'); // Assuming getStartDate() returns a \DateTime
  3444.             $endDate $notification->getEndDate()->format('Y-m-d H:i:s');
  3445.             $alertStatusEn $notification->getWeatherPhenomenon()?->getTitle("en");
  3446.             $creationDate $notification->getCreationDate(); // Assuming getCreationDate() returns a \DateTime
  3447.             $searchIdEn $notification->getEwsSearchId("en");
  3448.             $searchIdAr $notification->getEwsSearchId("ar");
  3449.             $title $notification->getStartDate() ? $notification->getStartDate()->format("dmY") . '-' $notification->getId() : '';
  3450.             $alertTypeEn $notification->getAlertType()?->getName("en");
  3451.             $fromDate $notification->getStartDate()->format('Y-m-d H:i:s');
  3452.             $toDate $notification->getEndDate()->format('Y-m-d H:i:s');
  3453.             $region $regionEn;
  3454.             $created_at $notification->getCreationDate();
  3455.             $alertStatus $alertStatusEn;
  3456.             $sortedData[] = [
  3457.                 'notification' => $notification,
  3458.                 'regionName' => $regionEn,
  3459.                 'alertStatus' => $alertStatusEn,
  3460.                 'startDate' => $startDate,
  3461.                 'endDate' => $endDate,
  3462.                 'creationDate' => $creationDate// Add creation date to sorting criteria
  3463.                 'searchIdEn' => $searchIdEn,
  3464.                 'searchIdAr' => $searchIdAr,
  3465.                 'title' => $title,
  3466.                 'alertType' => $alertTypeEn,
  3467.                 'fromDate' => $fromDate,
  3468.                 'toDate' => $toDate,
  3469.                 'region' => $region,
  3470.                 'created_at' => $created_at,
  3471.                 'alertStatus' => $alertStatus
  3472.             ];
  3473.         }
  3474.         // Define default sort orders and detect if any sort parameter is provided
  3475.         $sortOrderRegion $params['sortByRegion'] ?? null;
  3476.         $sortOrderStartDate $params['sortByStartDate'] ?? null;
  3477.         $sortOrderEndDate $params['sortByEndDate'] ?? null;
  3478.         $sortOrderAlertStatus $params['sortByAlertStatus'] ?? null;
  3479.         $sortOrderSearchIdEn $params['sortBySearchIdEn'] ?? null;
  3480.         $sortOrderSearchIdAr $params['sortBySearchIdAr'] ?? null;
  3481.         $sortOrderCreatedAt $params['sortByCreated'] ?? null;
  3482.         $sortOrderTitle $params['sortByTitle'] ?? null;
  3483.         $sortOrderAlertType $params['sortByAlertType'] ?? null;
  3484.         $sortOrderFromDate $params['sortByFromDate'] ?? null;
  3485.         $sortOrderToDate $params['sortByToDate'] ?? null;
  3486.         $sortOrderRegionNew $params['sortByRegionNew'] ?? null;
  3487.         $sortOrderCreatedAtNew $params['sortByCreatedAt'] ?? null;
  3488.         $sortOrderAlertStatusNew $params['sortByAlertStatusNew'] ?? null;
  3489.         
  3490.         // Support for orderKey and order parameters (NEW - clean approach)
  3491.         $orderKey $params['orderKey'] ?? null;
  3492.         $order $params['order'] ?? 'asc';
  3493.         
  3494.         // Simple mapping for new fields
  3495.         $newFieldMapping = [
  3496.             'title' => 'sortOrderTitle',
  3497.             'alertType' => 'sortOrderAlertType',
  3498.             'fromDate' => 'sortOrderFromDate'
  3499.             'toDate' => 'sortOrderToDate',
  3500.             'region' => 'sortOrderRegionNew',
  3501.             'created_at' => 'sortOrderCreatedAtNew',
  3502.             'alertStatus' => 'sortOrderAlertStatusNew'
  3503.         ];
  3504.         
  3505.         // Set the appropriate sort order variable
  3506.         if ($orderKey && isset($newFieldMapping[$orderKey])) {
  3507.             $variableName $newFieldMapping[$orderKey];
  3508.             $$variableName $order;
  3509.         }
  3510.         
  3511.         // Check if any sort parameter is provided (OLD + NEW)
  3512.         $anySortParamProvided $sortOrderRegion || $sortOrderStartDate || $sortOrderEndDate || $sortOrderAlertStatus || $sortOrderSearchIdEn || $sortOrderSearchIdAr || $sortOrderTitle || $sortOrderAlertType || $sortOrderFromDate || $sortOrderToDate || $sortOrderRegionNew || $sortOrderCreatedAtNew || $sortOrderAlertStatusNew;
  3513.         usort($sortedData, function ($a$b) use ($sortOrderRegion$sortOrderStartDate$sortOrderEndDate$sortOrderAlertStatus$sortOrderSearchIdEn$sortOrderSearchIdAr$sortOrderCreatedAt$sortOrderTitle$sortOrderAlertType$sortOrderFromDate$sortOrderToDate$sortOrderRegionNew$sortOrderCreatedAtNew$sortOrderAlertStatusNew$anySortParamProvided) {
  3514.             if (!$anySortParamProvided || $sortOrderCreatedAt) {
  3515.                 if ($sortOrderCreatedAt == null) {
  3516.                     $sortOrderCreatedAt "desc";
  3517.                 }
  3518.                 // Default to sorting by creation date if no sort parameter is provided
  3519.                 $compareCreatedAt strcmp($a['creationDate'], $b['creationDate']) * ($sortOrderCreatedAt === 'desc' ? -1);
  3520.                 if ($compareCreatedAt !== 0) return $compareCreatedAt// Assuming 'asc' as default sort order for creation date
  3521.             }
  3522.             // Else, sort based on the provided parameters
  3523.             if ($sortOrderRegion) {
  3524.                 $compareRegion strcmp($a['regionName'], $b['regionName']) * ($sortOrderRegion === 'desc' ? -1);
  3525.                 if ($compareRegion !== 0) return $compareRegion;
  3526.             }
  3527.             if ($sortOrderSearchIdEn) {
  3528.                 $compareSearchIdEn strcmp($a['searchIdEn'], $b['searchIdEn']) * ($sortOrderSearchIdEn === 'desc' ? -1);
  3529.                 if ($compareSearchIdEn !== 0) return $compareSearchIdEn;
  3530.             }
  3531.             if ($sortOrderSearchIdAr) {
  3532.                 $compareSearchIdAr strcmp($a['searchIdAr'], $b['searchIdAr']) * ($sortOrderSearchIdAr === 'desc' ? -1);
  3533.                 if ($compareSearchIdAr !== 0) return $compareSearchIdAr;
  3534.             }
  3535.             if ($sortOrderAlertStatus) {
  3536.                 $compareAlertStatus strcmp($a['alertStatus'], $b['alertStatus']) * ($sortOrderAlertStatus === 'desc' ? -1);
  3537.                 if ($compareAlertStatus !== 0) return $compareAlertStatus;
  3538.             }
  3539.             if ($sortOrderStartDate) {
  3540.                 $compareStartDate strcmp($a['startDate'], $b['startDate']) * ($sortOrderStartDate === 'desc' ? -1);
  3541.                 if ($compareStartDate !== 0) return $compareStartDate;
  3542.             }
  3543.             if ($sortOrderEndDate) {
  3544.                 return strcmp($a['endDate'], $b['endDate']) * ($sortOrderEndDate === 'desc' ? -1);
  3545.             }
  3546.             
  3547.             // New sorting fields (ADDED - keeps old functionality)
  3548.             if ($sortOrderTitle) {
  3549.                 $compareTitle strcmp($a['title'], $b['title']) * ($sortOrderTitle === 'desc' ? -1);
  3550.                 if ($compareTitle !== 0) return $compareTitle;
  3551.             }
  3552.             if ($sortOrderAlertType) {
  3553.                 $compareAlertType strcmp($a['alertType'], $b['alertType']) * ($sortOrderAlertType === 'desc' ? -1);
  3554.                 if ($compareAlertType !== 0) return $compareAlertType;
  3555.             }
  3556.             if ($sortOrderFromDate) {
  3557.                 $compareFromDate strcmp($a['fromDate'], $b['fromDate']) * ($sortOrderFromDate === 'desc' ? -1);
  3558.                 if ($compareFromDate !== 0) return $compareFromDate;
  3559.             }
  3560.             if ($sortOrderToDate) {
  3561.                 $compareToDate strcmp($a['toDate'], $b['toDate']) * ($sortOrderToDate === 'desc' ? -1);
  3562.                 if ($compareToDate !== 0) return $compareToDate;
  3563.             }
  3564.             if ($sortOrderRegionNew) {
  3565.                 $compareRegionNew strcmp($a['region'], $b['region']) * ($sortOrderRegionNew === 'desc' ? -1);
  3566.                 if ($compareRegionNew !== 0) return $compareRegionNew;
  3567.             }
  3568.             if ($sortOrderCreatedAtNew) {
  3569.                 $compareCreatedAtNew strcmp($a['created_at'], $b['created_at']) * ($sortOrderCreatedAtNew === 'desc' ? -1);
  3570.                 if ($compareCreatedAtNew !== 0) return $compareCreatedAtNew;
  3571.             }
  3572.             if ($sortOrderAlertStatusNew) {
  3573.                 $compareAlertStatusNew strcmp($a['alertStatus'], $b['alertStatus']) * ($sortOrderAlertStatusNew === 'desc' ? -1);
  3574.                 if ($compareAlertStatusNew !== 0) return $compareAlertStatusNew;
  3575.             }
  3576.             
  3577.             // If all provided parameters are equal, or no specific sort orders are provided, default to creation date
  3578.             return ($b['creationDate'] ?? 0) - ($a['creationDate'] ?? 0);
  3579.         });
  3580.         $sortedNotifications array_map(function ($item) {
  3581.             return $item['notification'];
  3582.         }, $sortedData);
  3583.         if (isset($params['dashboard']) && !empty($params['dashboard'] && $params['dashboard'] == 'dashboard')) {
  3584.             return ["data" => $notificationList];
  3585.         }
  3586.         // Extract the sorted notifications
  3587.        
  3588.         if ($paginator == null) {
  3589.             // if ($sortedNotifications->count()) {
  3590.             foreach ($sortedNotifications as $notification) {
  3591.                 // if($notification->getPublished())
  3592.                 $response[] = $this->createNotificationFormat($notification$translator);
  3593.             }
  3594.             // }
  3595.             return ["success" => TRUE"data" => $response];
  3596.         }
  3597.         $paginator $paginator->paginate(
  3598.             $sortedNotifications,
  3599.             $page,
  3600.             $pageSize
  3601.         );
  3602.         // if ($sortedNotifications->count()) {
  3603.         foreach ($paginator as $notification) {
  3604.             // if($notification->getPublished())
  3605.             $response[] = $this->createNotificationFormat($notification$translator);
  3606.         }
  3607.         // }
  3608.         return ["data" => $response"paginationVariables" => $paginator->getPaginationData()];
  3609.     }
  3610.     public function archiveNotification($params$translator): array
  3611.     {
  3612.         $id $params['id'] ?? null;
  3613.         if (!$id) {
  3614.             throw new \Exception("EWS id is required");
  3615.         }
  3616.         $ewsNotification \Pimcore\Model\DataObject::getById($id);
  3617.         if (!$ewsNotification instanceof \Pimcore\Model\DataObject\EwsNotification) {
  3618.             // throw new \Exception("Ews notification not found");
  3619.             return ['success' => false'message' => $translator->trans('ews_notification_not_found')];
  3620.         }
  3621.         if (isset($params['status']) && $params['status']) {
  3622.             $ewsNotification->setStatus("archived");
  3623.         }
  3624.         $ewsNotification->save(["versionNote" => "Update"]);
  3625.         return ['success' => true'message' => $translator->trans('archive_ews_notification'), "notification_id" => $ewsNotification->getId()];
  3626.     }
  3627.     public function getId(): ?UuidV4
  3628.     {
  3629.         $uuid UuidV4::v4();
  3630.         return $uuid;
  3631.     }
  3632.     public function getPhenomenaList()
  3633.     {
  3634.         $phenomenas = new \Pimcore\Model\DataObject\PhenomenaList\Listing();
  3635.         $phenomenas->setOrderKey("o_creationDate");
  3636.         $phenomenas->setOrder("desc");
  3637.         $phenomenas->load();
  3638.         $response = [];
  3639.         if (count($phenomenas) > 0) {
  3640.             foreach ($phenomenas as $phenomena) {
  3641.                 $response[] = [
  3642.                     "id" => $phenomena->getPhenomenaListId(),
  3643.                     "nameEN" => $phenomena->getTitle('en'),
  3644.                     "nameAr" => $phenomena->getTitle('ar'),
  3645.                 ];
  3646.             }
  3647.             return ["success" => true"data" => $response];
  3648.         }
  3649.         return ["success" => false"data" => $response];
  3650.     }
  3651.     public function getHazardListByAlertStatusId($statusId$lang "en")
  3652.     {
  3653.         $response = [];
  3654.         $alertSttaus \Pimcore\Model\DataObject\AlertStatus::getByAlertStatusId($statusIdtrue);
  3655.         if ($alertSttaus instanceof \Pimcore\Model\DataObject\AlertStatus) {
  3656.             if ($alertSttaus && $alertSttaus->getAlertHazards()) {
  3657.                 foreach ($alertSttaus->getAlertHazards() as  $value) {
  3658.                     $alertHazardObj $value->getLocalizedfields()->getItems();
  3659.                     $response[] = ["id" => (int)$value->getAlertHazardId(), "nameEn" => $alertHazardObj['en']['name'], "nameAr" => $alertHazardObj['ar']['name']];
  3660.                 }
  3661.             } else {
  3662.                 return ["success" => false"message" => "No Hazard is set"];
  3663.             }
  3664.         } else {
  3665.             return ["success" => false"message" => "Invalid alert status id"];
  3666.         }
  3667.         return ["success" => true"data" => $response];
  3668.     }
  3669.     // public function getHazardListByAlertStatusId($statusId, $lang = "en")
  3670.     // {
  3671.     //     $response = [];
  3672.     //     $alertPhenomena = \Pimcore\Model\DataObject\PhenomenaList::getByPhenomenaListId($statusId, true);
  3673.     //     if ($alertPhenomena instanceof \Pimcore\Model\DataObject\PhenomenaList) {
  3674.     //         if ($alertPhenomena && $alertPhenomena->getAlertHazards()) {
  3675.     //             foreach ($alertPhenomena->getAlertHazards() as  $value) {
  3676.     //                 $alertHazardObj = $value->getLocalizedfields()->getItems();
  3677.     //                 $response[] = ["id" => (int)$value->getAlertHazardId(), "nameEn" => $alertHazardObj['en']['name'], "nameAr" => $alertHazardObj['ar']['name']];
  3678.     //             }
  3679.     //         } else {
  3680.     //             return ["success" => false, "message" => "No Hazard is set"];
  3681.     //         }
  3682.     //     } else {
  3683.     //         return ["success" => false, "message" => "Invalid alert status id"];
  3684.     //     }
  3685.     //     return ["success" => true, "data" => $response];
  3686.     // }
  3687.     public function getOtherGovernoratesByRegion($regionId)
  3688.     {
  3689.         $response = [];
  3690.         $region \Pimcore\Model\DataObject\Region::getByRegionId($regionIdtrue);
  3691.         if (!$region) {
  3692.             throw new \Exception("Region not available");
  3693.         }
  3694.         $otherGovernorates = new DataObject\EwsOtherLocation\Listing();
  3695.         //$otherGovernorates->filterByRegionId($region);
  3696.         $otherGovernorates $otherGovernorates->load();
  3697.         if ($otherGovernorates) {
  3698.             foreach ($otherGovernorates as $governorate) {
  3699.                 $region $governorate->getregionId();
  3700.                 $regionData = [];
  3701.                 if (!empty($region)) {
  3702.                     $regionData = [
  3703.                         "id" => $region->getRegionId(),
  3704.                         "nameEn" => $region->getName('en'),
  3705.                         "nameAr" => $region->getName('ar'),
  3706.                         "longitude" => $region->getLongitude(),
  3707.                         "latitude" => $region->getLongitude()
  3708.                     ];
  3709.                 }
  3710.                 $response[] = [
  3711.                     "id" => $governorate->getgovernoteId(),
  3712.                     "nameEn" => $governorate->getName('en'),
  3713.                     "nameAr" => $governorate->getName('ar'),
  3714.                     "regionId" => $regionData,
  3715.                     "longitude" => $governorate->getLongitude(),
  3716.                     "latitude" => $governorate->getLatitude(),
  3717.                     "isHidden" => $governorate->getisHidden(),
  3718.                     "IsMunicipality" => $governorate->getIsMunicipality(),
  3719.                     "MunicipalityID" => $governorate->getMunicipalityID()
  3720.                 ];
  3721.             }
  3722.         }
  3723.         return ["success" => true"data" => $response];
  3724.     }
  3725.     public function createEwsReportUserGroup($params$translator)
  3726.     {
  3727.         $response = [];
  3728.         $checkGroup EwsAndReportUserGroup::getByName($params['name'], true);
  3729.         if ($checkGroup instanceof EwsAndReportUserGroup) {
  3730.             return ["success" => false"message" => $translator->trans("group_name_already_exists"), "name" => $params['name']];
  3731.         }
  3732.         $ewsReportUserGroup = new DataObject\EwsAndReportUserGroup();
  3733.         $ewsReportUserGroup->setParent(\Pimcore\Model\DataObject\Service::createFolderByPath("/Notification/userGroups/" $params['type'] . "/"));
  3734.         $ewsReportUserGroup->setKey(\Pimcore\Model\Element\Service::getValidKey($params['name'] . "-" strtotime("now") . "-" rand(110000), 'object''object'));
  3735.         $ewsReportUserGroup->setName(strip_tags($params['name']));
  3736.         $ewsReportUserGroup->setJsonData(json_encode($params['data']));
  3737.         $ewsReportUserGroup->setGroupType($params['type']);
  3738.         $ewsReportUserGroup->setPublished(true);
  3739.         $ewsReportUserGroup->save();
  3740.         return ["success" => true"message" => $translator->trans("ews_report_user_group_created")];
  3741.     }
  3742.     public function updateEwsReportUserGroup($params$translator)
  3743.     {
  3744.         $response = [];
  3745.         $updateGroup EwsAndReportUserGroup::getById($params['id']);
  3746.         if (!$updateGroup instanceof EwsAndReportUserGroup) {
  3747.             return ["success" => false"message" => $translator->trans('ews_report_user_group_not_found'), "id" => $params['id']];
  3748.         }
  3749.         if (isset($params['name']) && !empty($params['name']) && $updateGroup->getName() != $params['name']) {
  3750.             $checkGroup EwsAndReportUserGroup::getByName($params['name'], true);
  3751.             if ($checkGroup instanceof EwsAndReportUserGroup) {
  3752.                 return ["success" => false"message" => $translator->trans("group_name_already_exists"), "name" => $params['name']];
  3753.             }
  3754.             $updateGroup->setName(strip_tags($params['name']));
  3755.             $updateGroup->setKey(\Pimcore\Model\Element\Service::getValidKey($params['name'] . "-" strtotime("now") . "-" rand(110000), 'object''object'));
  3756.         }
  3757.         if (isset($params['data']) && !empty($params['data'])) {
  3758.             $updateGroup->setJsonData(json_encode($params['data']));
  3759.         }
  3760.         if (isset($params['type']) && !empty($params['type'])) {
  3761.             $updateGroup->setGroupType($params['type']);
  3762.             $updateGroup->setParent(\Pimcore\Model\DataObject\Service::createFolderByPath("/Notification/userGroups/" $params['type'] . "/"));
  3763.         }
  3764.         $updateGroup->setJsonData(json_encode($params['data']));
  3765.         $updateGroup->setGroupType($params['type']);
  3766.         $updateGroup->setPublished(true);
  3767.         $updateGroup->save();
  3768.         return ["success" => true"message" => $translator->trans("ews_report_user_group_updated")];
  3769.     }
  3770.     public function deleteEwsReportUserGroup($params$translator)
  3771.     {
  3772.         $response = [];
  3773.         $deleteGroup EwsAndReportUserGroup::getById($params['id']);
  3774.         if (!$deleteGroup instanceof EwsAndReportUserGroup) {
  3775.             return ["success" => false"message" => $translator->trans('ews_report_user_group_not_found'), "id" => $params['id']];
  3776.         }
  3777.         $deleteGroup->delete();
  3778.         return ["success" => true"message" => $translator->trans("ews_report_user_group_deleted")];
  3779.     }
  3780.     public function listEwsReportUserGroup($params$paginator$translator)
  3781.     {
  3782.         $data = [];
  3783.         $listing = new EwsAndReportUserGroup\Listing();
  3784.     
  3785.         // Allowed sorting keys
  3786.         $allowedOrderKeys = ['name''type''email_count''oo_id'];
  3787.         $allowedOrderDirections = ['asc''desc'];
  3788.     
  3789.         $orderKey $params['orderKey'] ?? 'oo_id';
  3790.         $orderDirection strtolower($params['order'] ?? 'desc');
  3791.     
  3792.         if (in_array($orderKey$allowedOrderKeys) && in_array($orderDirection$allowedOrderDirections)) {
  3793.             $fieldMapping = [
  3794.                 'name' => 'name',
  3795.                 'type' => 'groupType',
  3796.                 'oo_id' => 'oo_id'
  3797.             ];
  3798.     
  3799.             // Normal sorting keys (done in SQL)
  3800.             if (isset($fieldMapping[$orderKey])) {
  3801.                 $listing->setOrderKey($fieldMapping[$orderKey]);
  3802.                 $listing->setOrder(strtoupper($orderDirection));
  3803.             }
  3804.         } else {
  3805.             // Default sorting
  3806.             $orderKey 'oo_id';
  3807.             $orderDirection 'desc';
  3808.             $listing->setOrderKey('oo_id');
  3809.             $listing->setOrder('DESC');
  3810.         }
  3811.     
  3812.         if (isset($params['type']) && !empty($params['type'])) {
  3813.             $listing->addConditionParam("groupType = ?", [$params['type']]);
  3814.         }
  3815.         if (isset($params['search']) && !empty($params['search'])) {
  3816.             $listing->addConditionParam("name LIKE ? ", ['%' $params['search'] . '%']);
  3817.         }
  3818.         
  3819.     
  3820.         $pageSize = isset($params['page_size']) ? $params['page_size'] : LIMIT_PER_PAGE;
  3821.         $page = isset($params['page']) ? $params['page'] : 1;
  3822.     
  3823.         $paginator $paginator->paginate(
  3824.             $listing,
  3825.             $page,
  3826.             $pageSize
  3827.         );
  3828.     
  3829.         // Convert paginator to array
  3830.         $groups iterator_to_array($paginator);
  3831.     
  3832.         // Handle email_count sorting manually in PHP
  3833.         if ($orderKey === 'email_count') {
  3834.             usort($groups, function ($a$b) use ($orderDirection) {
  3835.                 $lenA count(json_decode($a->getJsonData(), true) ?? []);
  3836.                 $lenB count(json_decode($b->getJsonData(), true) ?? []);
  3837.                 return $orderDirection === 'asc' $lenA <=> $lenB $lenB <=> $lenA;
  3838.             });
  3839.         }
  3840.     
  3841.         // Prepare output data
  3842.         foreach ($groups as $group) {
  3843.             $jsonData json_decode($group->getJsonData(), true) ?? [];
  3844.             $data[] = [
  3845.                 "id" => $group->getId(),
  3846.                 "name" => $group->getName(),
  3847.                 "type" => $group->getGroupType(),
  3848.                 "email_count" => count($jsonData),
  3849.                 "data" => $jsonData
  3850.             ];
  3851.         }
  3852.         if (count($data) > 0) {
  3853.             return ["success" => true"data" => $data"paginationVariables" => $paginator->getPaginationData()];
  3854.         } else {
  3855.             return ["success" => false"message" => $translator->trans('no_ews_report_user_group_found')];
  3856.         }
  3857.     }
  3858.     public function getSortCriteria()
  3859.     {
  3860.         // This is a placeholder; you'll need to adapt this based on how you can access the region's name or other criteria
  3861.         return $this->getRegion()->getName();
  3862.     }
  3863.     public function getCenters($governorateId null$lang 'en')
  3864.     {
  3865.         $response = [];
  3866.         $districts = new DataObject\District\Listing();
  3867.         if (is_array($governorateId)) {
  3868.             $govIdsArr = [];
  3869.             foreach ($governorateId as $govId) {
  3870.                 $governorate \Pimcore\Model\DataObject\Governorate::getByGovernoteId($govIdtrue);
  3871.                 if ($governorate) {
  3872.                     array_push($govIdsArr$governorate->getId());
  3873.                 }
  3874.             }
  3875.             $districts->setCondition("governorate__id IN (" implode(", "$govIdsArr) . ")");
  3876.         } else {
  3877.             if ($governorateId) {
  3878.                 $governorate \Pimcore\Model\DataObject\Governorate::getByGovernoteId($governorateIdtrue);
  3879.                 $districts->setCondition("governorate__id = ?", [$governorate->getId()]);
  3880.             }
  3881.         }
  3882.         $districts $districts->load();
  3883.         if ($districts) {
  3884.             foreach ($districts as $district) {
  3885.                 $response[] = [
  3886.                     "id" => $district->getObjectId(),
  3887.                     "nameEn" => $district->getName('en'),
  3888.                     "nameAr" => $district->getName('ar'),
  3889.                     "longitude" => $district->getLongitude(),
  3890.                     "latitude" => $district->getLatitude(),
  3891.                     "governate" => $district->getGovernorate()->getGovernoteId()
  3892.                 ];
  3893.             }
  3894.         }
  3895.         // Determine sorting field based on language
  3896.         $sortField 'nameEn'// Default sorting by English
  3897.         if (isset($lang) && strtolower($lang) === 'ar') {
  3898.             $sortField 'nameAr'// Sorting by Arabic
  3899.         }
  3900.         // Sort manually using usort()
  3901.         usort($response, function ($a$b) use ($sortField) {
  3902.             return strcmp($a[$sortField], $b[$sortField]);
  3903.         });
  3904.         return ["success" => true"data" => $response];
  3905.     }
  3906. }