src/Controller/PublicApiController.php line 3138

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\C2IntegrationBundle\Service\C2Service;
  4. use Carbon\Carbon;
  5. use Knp\Snappy\Pdf;
  6. use Knp\Snappy\Image;
  7. use Pimcore\Model\Asset;
  8. use App\Service\RedisCache;
  9. use App\Service\CfoReportPayloadBuilder;
  10. use App\Model\ReportLogModel;
  11. use App\Service\EmailService;
  12. use App\Service\ReportService;
  13. use DateTime;
  14. use Pimcore\Log\ApplicationLogger;
  15. use App\Model\EwsNotificationModel;
  16. use App\Service\NotificationService;
  17. use Pimcore\Model\DataObject\Report;
  18. use App\Service\MeteomaticApiService;
  19. use App\Model\WeatherForecastCityModel;
  20. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  21. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  22. use App\Service\CustomNotificationService;
  23. use App\Service\MeteomaticsWeatherService;
  24. use Pimcore\Controller\FrontendController;
  25. use Knp\Component\Pager\PaginatorInterface;
  26. use App\Service\PublicUserPermissionService;
  27. use App\Service\NCMWeatherAPIService;
  28. use GuzzleHttp\Client;
  29. use App\Model\ReportingPortalModel;
  30. use Symfony\Component\HttpFoundation\Request;
  31. use Symfony\Component\HttpFoundation\Response;
  32. use Symfony\Component\Routing\Annotation\Route;
  33. use Pimcore\Model\DataObject\ReportWeatherSymbols;
  34. use Symfony\Component\HttpFoundation\JsonResponse;
  35. use Symfony\Contracts\Translation\TranslatorInterface;
  36. use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
  37. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  38. use Symfony\Component\Templating\EngineInterface;
  39. use App\Service\InsuranceIndustryService;
  40. use App\Model\WeatherStationModel;
  41. use App\Service\WeatherStationService;
  42. use Pimcore\Model\DataObject\HistoricalDataParameters;
  43. use App\Model\AgricultureModel;
  44. use App\Service\ChillUnitService;
  45. use Pimcore\Model\DataObject;
  46. use App\Model\LocationModel;
  47. use App\Model\WeatherParameterModel;
  48. use Pimcore\Model\DataObject\WeatherParameterLocationTags;
  49. use GuzzleHttp\Exception\RequestException;
  50. use App\Service\Translation\TranslationServiceInterface;
  51. use App\Model\MarineReportModel;
  52. class PublicApiController extends FrontendController
  53. {
  54.     private $ewsNotificationModel;
  55.     private $reportModel;
  56.     private $c2Service;
  57.     private $reportingPortalModel;
  58.     private $weatherStationModel;
  59.     private $agricultureModel;
  60.     private $chillUnitService;
  61.     private $locationModel;
  62.     private $weatherParameterModel;
  63.     private $httpClient;
  64.     private $marineReportModel;
  65.     public function __construct(
  66.         private TokenStorageInterface $tokenStorageInterface,
  67.         private JWTTokenManagerInterface $jwtManager,
  68.         private PublicUserPermissionService $publicUserPermissionService,
  69.         protected TranslatorInterface $translator,
  70.         private ApplicationLogger $logger,
  71.         private MeteomaticApiService $meteomaticApiService,
  72.         private \Doctrine\DBAL\Connection $connection,
  73.         private RedisCache $redisCache,
  74.         private MeteomaticsWeatherService $meteomaticsWeatherService,
  75.         private Pdf $snappy,
  76.         private CustomNotificationService $customNotificationService,
  77.         private ReportService $reportService,
  78.         private NotificationService $notificationService,
  79.         private Image $snappyImage,
  80.         private EmailService $emailService,
  81.         private EngineInterface $templating,
  82.         private NCMWeatherAPIService $ncmWeatherApiService,
  83.         private InsuranceIndustryService $insuranceIndustryService,
  84.         private WeatherStationService $weatherStationService,
  85.         private CfoReportPayloadBuilder $cfoReportPayloadBuilder,
  86.         private TranslationServiceInterface $marineTranslator,
  87.     ) {
  88.         header('Content-Type: application/json; charset=UTF-8');
  89.         // header("Access-Control-Allow-Origin: *");
  90.         $this->meteomaticApiService $meteomaticApiService;
  91.         $this->publicUserPermissionService $publicUserPermissionService;
  92.         $this->ewsNotificationModel = new EwsNotificationModel();
  93.         $this->reportModel = new ReportLogModel();
  94.         $this->c2Service = new C2Service();
  95.         $this->templating =  $templating;
  96.         $this->reportingPortalModel = new ReportingPortalModel();
  97.         $this->weatherStationModel = new WeatherStationModel();
  98.         $this->agricultureModel = new AgricultureModel();
  99.         $this->chillUnitService = new ChillUnitService();
  100.         $this->locationModel = new LocationModel();
  101.         $this->weatherParameterModel = new WeatherParameterModel();
  102.         $this->marineReportModel = new MarineReportModel();
  103.         $this->httpClient = new Client([
  104.             'verify' => false // Disable SSL verification
  105.         ]);
  106.     }
  107.     /**
  108.      * @Route("/api/public/{startdate}/{enddate}/{resolution}/{parameter}/{coordinate}/{format}", name="api_public_route_query", methods={"GET"})
  109.      */
  110.     public function publicRouteQueryData(Request $request): JsonResponse
  111.     {
  112.         try {
  113.             // check user credentials and expiry
  114.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  115.             if ($response['success'] !== true) {
  116.                 return $this->json($response);
  117.             }
  118.             $startDate $request->get('startdate');
  119.             $endDate $request->get('enddate');
  120.             $resolution $request->get('resolution');
  121.             $parameter $request->get('parameter');
  122.             $coordinate $request->get('coordinate');
  123.             $format $request->get('format');
  124.             // Optional Parameter
  125.             $source $request->get('source');
  126.             $onInvalid $request->get('on_invalid');
  127.             $model $request->get('model');
  128.             if (!$startDate) {
  129.                 throw new \InvalidArgumentException("Missing mandatory parameter: start date");
  130.             }
  131.             if (!$endDate) {
  132.                 throw new \InvalidArgumentException("Missing mandatory parameter: end date");
  133.             }
  134.             if (!$resolution) {
  135.                 throw new \InvalidArgumentException("Missing mandatory parameter: resolution");
  136.             }
  137.             if (!$parameter) {
  138.                 throw new \InvalidArgumentException("Missing mandatory parameter: parameters");
  139.             }
  140.             if (!$coordinate) {
  141.                 throw new \InvalidArgumentException("Missing mandatory parameter: coordinate");
  142.             }
  143.             if (!$format) {
  144.                 throw new \InvalidArgumentException("Missing mandatory parameter: format");
  145.             }
  146.             $startDate = new DateTime($startDate);
  147.             $endDate = new DateTime($endDate);
  148.             $user $response['user'];
  149.             $parametersArray explode(','$parameter);
  150.             // Varify user allowed permissions
  151.             // $reslult = $this->publicUserPermissionService->publicUserPermissionCheck($user, $parametersArray, $this->translator);
  152.             // if ($reslult['success'] !== true) {
  153.             //     return $this->json($reslult);
  154.             // }
  155.             $response $this->meteomaticApiService->publicRouteQuery(
  156.                 $startDate,
  157.                 $endDate,
  158.                 $resolution,
  159.                 $parametersArray,
  160.                 $coordinate,
  161.                 $format,
  162.                 $model,
  163.                 $source,
  164.                 $onInvalid
  165.             );
  166.             // For demonstration purposes, we will just return a JSON response
  167.             return $this->json($response);
  168.         } catch (\Exception $ex) {
  169.             $this->logger->error($ex->getMessage());
  170.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  171.         }
  172.     }
  173.     /**
  174.      * @Route("/api/public/cfo-report", name="public_cfo_report", methods={"POST"})
  175.      * @Route("/api/public/forecast-report", name="public_cfo_forecast_report", methods={"POST"})
  176.      */
  177.     public function getCfoReport(Request $request): JsonResponse
  178.     {
  179.         try {
  180.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  181.             if ($permissions['success'] !== true) {
  182.                 return $this->json($permissions);
  183.             }
  184.             $params json_decode($request->getContent(), true) ?: [];
  185.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  186.             $reportType '10-day-forecast-report';
  187.             $ccode $params['ccode'] ?? null;
  188.             $isCfo $this->parseRequestBoolParam($paramstrue'isCFO''isCfo''is_cfo');
  189.             $latest $this->reportingPortalModel->getLatestReport([
  190.                 'reportType' => $reportType,
  191.             ]);
  192.             if (!is_array($latest) || !isset($latest['success']) || $latest['success'] !== true || !isset($latest['data'])) {
  193.                 return $this->json(['success' => false'message' => 'Failed to fetch latest report']);
  194.             }
  195.             $latestData $latest['data'];
  196.             $jsonDataFull = isset($latestData['jsonData']) && is_array($latestData['jsonData']) ? $latestData['jsonData'] : [];
  197.             if ($jsonDataFull === []) {
  198.                 return $this->json(['success' => false'message' => 'Empty jsonData in latest report']);
  199.             }
  200.             $filterByNames = [];
  201.             if (isset($params['filterByNames']) && is_array($params['filterByNames'])) {
  202.                 $filterByNames $params['filterByNames'];
  203.             } elseif (isset($params['filter_by_names']) && is_array($params['filter_by_names'])) {
  204.                 $filterByNames $params['filter_by_names'];
  205.             }
  206.             $hasNameFilter $filterByNames !== [];
  207.             $jsonDataForMerge $hasNameFilter
  208.                 $this->filterJsonDataByNames($jsonDataFull$filterByNames)
  209.                 : $jsonDataFull;
  210.             $latestForMerge $latest;
  211.             if ($hasNameFilter) {
  212.                 $filteredLatestData $latestData;
  213.                 $filteredLatestData['jsonData'] = $jsonDataForMerge;
  214.                 $latestForMerge['data'] = $filteredLatestData;
  215.             }
  216.             $reportId $latestData['id'] ?? 0;
  217.             $redisKey CfoReportPayloadBuilder::redisKey($reportType$ccode$reportId$isCfo);
  218.             // Cache-aside: the cached payload is already normalized (weatherinfo flags + current-param
  219.             // fallbacks applied at write time), so a cache hit can return it directly without re-running
  220.             // normalizeJsonDataCurrentFlags(). On miss we build, normalize once, then store.
  221.             // Skip cache when filterByNames is set — cached payload is the full report, not a name-filtered subset.
  222.             $cachedFinalData null;
  223.             if (!$hasNameFilter) {
  224.                 try {
  225.                     $cachedFinalData $this->redisCache->get($redisKey);
  226.                 } catch (\Throwable $redisEx) {
  227.                     $this->logger->warning('CFO report cache read failed; building without cache: ' $redisEx->getMessage());
  228.                 }
  229.             }
  230.             $cacheLooksValid is_array($cachedFinalData)
  231.                 && isset($cachedFinalData['jsonData'])
  232.                 && is_array($cachedFinalData['jsonData'])
  233.                 && $cachedFinalData['jsonData'] !== [];
  234.             if ($cacheLooksValid) {
  235.                 // Cache hit: payload was normalized before storage — return as-is.
  236.                 $finalData $cachedFinalData;
  237.                 $mergedJsonData $finalData['jsonData'];
  238.             } else {
  239.                 $finalData null;
  240.                 $builtFromMerge false;
  241.                 try {
  242.                     $built $this->cfoReportPayloadBuilder->buildMergedFinalDataFromLatest($ccode$latestForMerge$isCfo);
  243.                     if (isset($built['success']) && $built['success'] === true && isset($built['data'])) {
  244.                         $finalData $built['data'];
  245.                         $builtFromMerge true;
  246.                     } else {
  247.                         $this->logger->warning('CFO report merge failed; falling back to report data: ' . ($built['message'] ?? 'Unknown error'));
  248.                     }
  249.                 } catch (\Throwable $cfoEx) {
  250.                     $this->logger->warning('CFO report merge failed; falling back to report data: ' $cfoEx->getMessage());
  251.                 }
  252.                 if ($finalData === null) {
  253.                     $finalData $latestData;
  254.                     $finalData['jsonData'] = $jsonDataForMerge;
  255.                 }
  256.                 // Normalize once, before caching, so subsequent cache hits skip this step entirely.
  257.                 $mergedJsonData $this->cfoReportPayloadBuilder->normalizeJsonDataCurrentFlags(
  258.                     $finalData['jsonData'],
  259.                     $isCfo
  260.                 );
  261.                 $finalData['jsonData'] = $mergedJsonData;
  262.                 // Only cache successful merges (not the report-data fallback) and not name-filtered subsets.
  263.                 if ($builtFromMerge && !$hasNameFilter) {
  264.                     try {
  265.                         $this->redisCache->set($redisKey$finalDataCFO_REPORT_CACHE_TTL);
  266.                     } catch (\Throwable $redisEx) {
  267.                         $this->logger->warning('CFO report cache write failed; returning fresh data anyway: ' $redisEx->getMessage());
  268.                     }
  269.                 }
  270.             }
  271.             $searchTerm = isset($params['search']) ? trim($params['search']) : null;
  272.             if ($searchTerm !== null && $searchTerm !== '') {
  273.                 $searchTermLower mb_strtolower($searchTerm);
  274.                 $filteredJsonData = [];
  275.                 foreach ($mergedJsonData as $cityData) {
  276.                     $cityEn $cityData['cityEn'] ?? '';
  277.                     $cityAr $cityData['cityAr'] ?? '';
  278.                     if (mb_strpos(mb_strtolower($cityEn), $searchTermLower) !== false ||
  279.                         mb_strpos(mb_strtolower($cityAr), $searchTermLower) !== false) {
  280.                         $filteredJsonData[] = $cityData;
  281.                     }
  282.                 }
  283.                 $mergedJsonData $filteredJsonData;
  284.             }
  285.             $searchByPhenomenon = isset($params['search_by_phenomenon']) ? trim($params['search_by_phenomenon']) : null;
  286.             if ($searchByPhenomenon !== null && $searchByPhenomenon !== '') {
  287.                 $phenomenonLower mb_strtolower($searchByPhenomenon);
  288.                 $filteredJsonData = [];
  289.                 foreach ($mergedJsonData as $cityData) {
  290.                     $currentParamsRow $cityData['currentParameters'] ?? [];
  291.                     $phenomenonAr $currentParamsRow['currentWeather_PhenomenonAr'] ?? '';
  292.                     $phenomenonEn $currentParamsRow['currentWeather_PhenomenonEn'] ?? '';
  293.                     if (mb_strpos(mb_strtolower($phenomenonEn), $phenomenonLower) !== false ||
  294.                         mb_strpos(mb_strtolower($phenomenonAr), $phenomenonLower) !== false) {
  295.                         $filteredJsonData[] = $cityData;
  296.                     }
  297.                 }
  298.                 $mergedJsonData $filteredJsonData;
  299.             }
  300.             $finalData['jsonData'] = $mergedJsonData;
  301.             return $this->json([
  302.                 'success' => true,
  303.                 'data' => $finalData,
  304.             ]);
  305.         } catch (\Exception $ex) {
  306.             $this->logger->error($ex->getMessage());
  307.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  308.         }
  309.     }
  310.     /**
  311.      * @Route("/api/public/wms/tile", name="api_public_wms_tile", methods={"GET"})
  312.      */
  313.     public function getPublicWmsTile(Request $request): Response
  314.     {
  315.         try {
  316.             $layers $request->query->get('layers');
  317.             $time $request->query->get('time');
  318.             $bbox $request->query->get('bbox');
  319.             if (empty($layers) || empty($time) || empty($bbox)) {
  320.                 return $this->json([
  321.                     'success' => false,
  322.                     'message' => 'Missing required parameters: layers, time, bbox'
  323.                 ], 400);
  324.             }
  325.             $format $request->query->get('format''image/webp');
  326.             $width = (int) $request->query->get('width'256);
  327.             $height = (int) $request->query->get('height'256);
  328.             $crs $request->query->get('crs''EPSG:3857');
  329.             $version $request->query->get('version''1.3.0');
  330.             $service $request->query->get('service''WMS');
  331.             $wmsRequest $request->query->get('request''GetMap');
  332.             $styles $request->query->get('styles''');
  333.             $transparent $request->query->get('transparent''true');
  334.             $model $request->query->get('model');
  335.             $webpQuality $request->query->get('webp_quality_factor');
  336.             // Build query string for upstream WMS
  337.             $queryParams = [
  338.                 'service' => $service,
  339.                 'request' => $wmsRequest,
  340.                 'layers' => $layers,
  341.                 'styles' => $styles,
  342.                 'format' => $format,
  343.                 'transparent' => $transparent,
  344.                 'version' => $version,
  345.                 'time' => $time,
  346.                 'width' => $width,
  347.                 'height' => $height,
  348.                 'crs' => $crs,
  349.                 'bbox' => $bbox,
  350.             ];
  351.             if (!empty($model)) {
  352.                 $queryParams['model'] = $model;
  353.             }
  354.             if (!empty($webpQuality)) {
  355.                 $queryParams['webp_quality_factor'] = $webpQuality;
  356.             }
  357.             // Ensure RFC3986 encoding for characters like "/" and ":"
  358.             $paramString http_build_query($queryParams'''&'PHP_QUERY_RFC3986);
  359.             // Fetch from Meteomatics service (auth handled by service)
  360.             $binary $this->meteomaticsWeatherService->getWms(['param' => $paramString]);
  361.             // Determine content type
  362.             $contentType strtolower($format);
  363.             if (strpos($contentType'image/') !== 0) {
  364.                 $contentType 'image/' ltrim($contentType'/');
  365.             }
  366.             $response = new Response($binary);
  367.             $response->headers->set('Content-Type'$contentType);
  368.             $response->headers->set('Cache-Control''public, max-age=300');
  369.             return $response;
  370.         } catch (\Throwable $ex) {
  371.             return $this->json(['success' => false'message' => $ex->getMessage()], 500);
  372.         }
  373.     }
  374.     /**
  375.      * @Route("/api/public/get-report-detail", name="public_get_report_detail")
  376.      */
  377.     public function getReportDetails(Request $request)
  378.     {
  379.         try {
  380.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  381.             if ($response['success'] !== true) {
  382.                 return $this->json($response);
  383.             }
  384.             $user $response['user'];
  385.             // $params = json_decode($request->getContent(), true);
  386.             $requestUri $request->getRequestUri();
  387.             // Extract the last segment from the URL
  388.             $segments explode('/'trim($requestUri'/'));
  389.             $action end($segments);
  390.             $action str_replace("-""_"$action);
  391.             $params[] = $action;
  392.             // $reslult = $this->publicUserPermissionService->publicUserPermissionCheck($user, $params, $this->translator);
  393.             // if ($reslult['success'] !== true) {
  394.             //     return $this->json($reslult);
  395.             // }
  396.             $latestReport Report::getList([
  397.                 "limit" => 1,
  398.                 "orderKey" => "createdOn",
  399.                 "order" => "desc"
  400.             ]);
  401.             if ($latestReport->getCount() <= 0) {
  402.                 throw new \Exception('no_latest_report_found');
  403.             } else {
  404.                 $data = [];
  405.                 foreach ($latestReport as $report) {
  406.                     $data = [
  407.                         'name' => $report->getCreatedBy()?->getName(),
  408.                         'email' => $report->getCreatedBy()?->getEmail(),
  409.                         'jsonData' => json_decode($report->getJsonData(), true),
  410.                         'createdOn' => $report->getCreatedOn()
  411.                     ];
  412.                 }
  413.                 return $this->json(['success' => true'data' => $data]);
  414.             }
  415.         } catch (\Exception $ex) {
  416.             $this->logger->error($ex->getMessage());
  417.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  418.         }
  419.     }
  420.     /**
  421.      * @Route("/api/public/ews-analytics", methods={"POST"})
  422.      */
  423.     public function ewsAnalyticsAction(Request $request): JsonResponse
  424.     {
  425.         try {
  426.             $params json_decode($request->getContent(), true);
  427.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  428.             $lang = ($request->headers->has('lang')) ? $request->headers->get('lang') : "en";
  429.             $result $this->ewsNotificationModel->ewsAnalytics($params$this->connection$lang);
  430.             return $this->json($result);
  431.         } catch (\Exception $ex) {
  432.             $this->logger->error($ex->getMessage());
  433.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  434.         }
  435.     }
  436.     /**
  437.      * @Route("/api/public/generate-report", methods={"POST"})
  438.      */
  439.     public function generateReport(Request $request): JsonResponse
  440.     {
  441.         try {
  442.             $response = [];
  443.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  444.             if ($permissions['success'] !== true) {
  445.                 return $this->json($permissions);
  446.             }
  447.             $params json_decode($request->getContent(), true);
  448.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  449.             if (
  450.                 !isset($params['from_date']) ||
  451.                 !isset($params['to_date']) ||
  452.                 !isset($params['hours']) ||
  453.                 !isset($params['parameters'])  ||
  454.                 !isset($params['locations'])
  455.             ) {
  456.                 throw new \Exception('Missing required parameters');
  457.             }
  458.             if (empty($params['parameters'] || !is_array($params['parameters']))) {
  459.                 throw new \Exception('Parameters should be non empty array');
  460.             }
  461.             if (empty($params['locations'] || !is_array($params['locations']))) {
  462.                 throw new \Exception('Locations should be non empty array');
  463.             }
  464.             $model = isset($params['model']) ? $params['model'] : 'mix';
  465.             $redisKey md5('generate_city_report-' $params['from_date'] . '-' $params['to_date'] . '-' $params['hours'] . '-' implode('_'$params['locations'])) . '-' implode('_'$params['parameters']) . '-' $model;
  466.             $data $this->redisCache->get($redisKey);
  467.             if (!$data) {
  468.                 $cities = new \Pimcore\Model\DataObject\City\Listing();
  469.                 if (!empty($params['locations'])) {
  470.                     $cities->setCondition('o_id IN (?)', [$params['locations']]);
  471.                 }
  472.                 $cities->load();
  473.                 if ($cities->getCount() > 0) {
  474.                     $params['coordinates'] = []; // Initialize an empty array for coordinates
  475.                     $result = [];
  476.                     $citiesArr = [];
  477.                     foreach ($cities as $city) {
  478.                         $params['coordinates'][] = [$city->getLatitude(), $city->getLongitude()];
  479.                         // Append the coordinates for each city
  480.                         $long number_format($city->getLongitude(), 6'.''');
  481.                         $lat number_format($city->getLatitude(), 6'.''');
  482.                         $citiesArr[$lat '|' $long]["en"] =  $city->getCityName("en");
  483.                         $citiesArr[$lat '|' $long]["ar"] =  $city->getCityName("ar");
  484.                     }
  485.                     $result $this->meteomaticsWeatherService->getReportForecastData($params['coordinates'], $params['from_date'], $params['to_date'], $params['hours'], $model$params['parameters'], $this->translator$citiesArr$params);
  486.                     $response[] = $result;
  487.                     $jsonResponse = ['success' => true'data' => $response];
  488.                     $this->redisCache->set($redisKey$jsonResponseREDIS_CACHE_TIME);
  489.                     return $this->json($jsonResponse);
  490.                 } else {
  491.                     return $this->json(['success' => true'message' => $this->translator->trans('no_city_found')]);
  492.                 }
  493.             } else {
  494.                 return $this->json($data);
  495.             }
  496.         } catch (\Exception $ex) {
  497.             $this->logger->error($ex->getMessage());
  498.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  499.         }
  500.     }
  501.     /**
  502.      * @Route("/api/public/update-report", methods={"POST"})
  503.      */
  504.     public function updateReport(Request $request)
  505.     {
  506.         try {
  507.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  508.             if ($permissions['success'] !== true) {
  509.                 return $this->json($permissions);
  510.             }
  511.             $user $permissions['user'];
  512.             $params  json_decode($request->getContent(), true);
  513.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  514.             // Perform parameter validation here
  515.             // || !isset($params['locations'])
  516.             if (!isset($params['data'])  || !isset($params['start_date']) || !isset($params['end_date']) || !isset($params['lang'])) {
  517.                 return $this->json(['success' => false'message' =>  $this->translator->trans('missing_required_parameters')]);
  518.             }
  519.             if (isset($params['organizations'])) {
  520.                 if (!is_array($params['organizations'])) {
  521.                     throw new \Exception($this->translator->trans('Organizations should be non empty array'), 400);
  522.                 }
  523.             }
  524.             if (isset($params['channels'])) {
  525.                 if (!is_array($params['channels']) || empty($params['channels'])) {
  526.                     throw new \Exception($this->translator->trans('Channels should be non empty array'), 400);
  527.                 }
  528.                 if (in_array('email'$params['channels'])) {
  529.                     if (!isset($params['emails']) || !is_array($params['emails']) || empty($params['emails'])) {
  530.                         throw new \Exception($this->translator->trans('Emails should be non empty array'), 400);
  531.                     }
  532.                 }
  533.             }
  534.             $result $this->reportModel->editReport($user$params$this->translator$this->logger);
  535.             return $this->json($result);
  536.         } catch (\Exception $ex) {
  537.             $this->logger->error($ex->getMessage());
  538.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  539.         }
  540.     }
  541.     /**
  542.      * @Route("/api/public/get-city-list", name="public-city-listing")
  543.      */
  544.     public function getCityListAction(Request $request)
  545.     {
  546.        
  547.         try {
  548.             $result = [];
  549.             $lang = ($request->headers->has('lang')) ? $request->headers->get('lang') : "en";
  550.             $params  json_decode($request->getContent(), true);
  551.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  552.             $reportTypeId $params['report_type_id'] ?? null;
  553.             $cities = new \Pimcore\Model\DataObject\City\Listing();
  554.             $cities->setOrderKey('cityName');
  555.             $cities->setOrder("ASC");
  556.             if ($reportTypeId) {
  557.                 $cities->setCondition("reportType REGEXP CONCAT('(^|,)', REPLACE('" $reportTypeId "', ',', '|'), '(,|$)')");
  558.             } else {
  559.                 $db \Pimcore\Db::get();
  560.                 $selectedLocalities $db->fetchAllAssociative("SELECT oo_id  FROM `object_query_ReportType` WHERE (`isAutomaticReport` = 0)");
  561.                 $ooIds array_column($selectedLocalities'oo_id');
  562.                 $cities->setCondition("reportType REGEXP CONCAT('(^|,)', REPLACE('" implode(','$ooIds) . "', ',', '|'), '(,|$)')");
  563.             }
  564.             $cities->load();
  565.             if ($cities->getCount() > 0) {
  566.                 foreach ($cities as $city) {
  567.                     $result[] = [
  568.                         "id" => $city->getId(),
  569.                         "name" => $city->getCityName($lang),
  570.                         "nameEn" => $city->getCityName('en'),
  571.                         "nameAr" => $city->getCityName('ar'),
  572.                         "lat" => $city->getLatitude(),
  573.                         "long" => $city->getLongitude(),
  574.                         "googlePlaceName" => $city->getGooglePlaceName(),
  575.                     ];
  576.                 }
  577.                 return $this->json(["success" => true"data" => $result]);
  578.             }
  579.             return $this->json(["success" => false"message" => $this->translator->trans("no_city_is_available")]);
  580.         } catch (\Exception $ex) {
  581.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  582.         }
  583.     }
  584.     /**
  585.      * @Route("/api/public/list-report", name="public-report-listing")
  586.      */
  587.     public function getReportListAction(Request $requestPaginatorInterface $paginator)
  588.     {
  589.         try {
  590.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  591.             if ($permissions['success'] !== true) {
  592.                 return $this->json($permissions);
  593.             }
  594.             $user $permissions['user'];
  595.             $params  json_decode($request->getContent(), true);
  596.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  597.             if (!isset($params['page']) || !isset($params['limit'])) {
  598.                 throw new \Exception('Missing required params: page or limit');
  599.             }
  600.             $report_type = isset($params['report_type']) ? $params['report_type'] : null;
  601.             $lang = isset($params['lang']) ? $params['lang'] : DEFAULT_LOCALE;
  602.             $isPublicReports = isset($params['publicReports']) ? $params['publicReports'] : false// sending this flag from public portal to get all reports accordong to our needs
  603.             $page $params['page'];
  604.             $limit $params['limit'];
  605.             $result $this->reportModel->reportList($params$page$limit$this->translator$paginator$report_type$user$lang$isPublicReports);
  606.             return $this->json($result);
  607.         } catch (\Exception $ex) {
  608.             $this->logger->error($ex->getMessage());
  609.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  610.         }
  611.     }
  612.     /**
  613.      * @Route("/api/public/generate-report-pdf", name="public-report-pdf")
  614.      */
  615.     public function generateReportPdf(Request $request)
  616.     {
  617.         try {
  618.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  619.             if ($permissions['success'] !== true) {
  620.                 return $this->json($permissions);
  621.             }
  622.             $user $permissions['user'];
  623.             $params  json_decode($request->getContent(), true);
  624.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  625.             return $this->getPdfReport($request$user'pdf/report_pdf_template.html.twig'$this->translator'_manned_forecast_report.pdf');
  626.         } catch (\Exception $ex) {
  627.             $this->logger->error($ex->getMessage());
  628.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  629.         }
  630.     }
  631.     /**
  632.      * @Route("/api/public/get-report-types", name="public-report-types-listing")
  633.      */
  634.     public function getReportTypes(Request $request)
  635.     {
  636.         try {
  637.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  638.             if ($permissions['success'] !== true) {
  639.                 return $this->json($permissions);
  640.             }
  641.             $params  json_decode($request->getContent(), true);
  642.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  643.             if (!isset($params['automatic'])) {
  644.                 throw new \Exception('missing_required_parameters');
  645.             }
  646.             $result = [];
  647.             $automatic $params['automatic'] ? true false;
  648.             $reportTypes = new \Pimcore\Model\DataObject\ReportType\Listing();
  649.             $reportTypes->setCondition('isAutomaticReport = ?', [$automatic]);
  650.             $reportTypes->load();
  651.             if ($reportTypes->getCount() > 0) {
  652.                 foreach ($reportTypes as $reportType) {
  653.                     $result[] = [
  654.                         "id" => $reportType->getId(),
  655.                         "key" => $reportType->getReportKey(),
  656.                         "nameEn" => $reportType->getName('en'),
  657.                         "nameAr" => $reportType->getName('ar'),
  658.                         "descriptionEn" => $reportType->getDescription('en'),
  659.                         "descriptionAr" => $reportType->getDescription('ar'),
  660.                         "titleEn" => $reportType->getTitle('en'),
  661.                         "titleAr" => $reportType->getTitle('ar'),
  662.                         "automatic" => $reportType->getIsAutomaticReport()
  663.                     ];
  664.                 }
  665.                 return $this->json(["success" => true"data" => $result]);
  666.             }
  667.             return $this->json(["success" => false"message" => $this->translator->trans("no_reportType_is_available")]);
  668.         } catch (\Exception $ex) {
  669.             $this->logger->error($ex->getMessage());
  670.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  671.         }
  672.     }
  673.     /**
  674.      * @Route("/api/public/get-weather-params-list", name="public-weather-params-listing")
  675.      */
  676.     public function getWeatherParamsListAction(Request $request)
  677.     {
  678.         try {
  679.             $result = [];
  680.             $lang = ($request->headers->has('lang')) ? $request->headers->get('lang') : "en";
  681.             $params  json_decode($request->getContent(), true);
  682.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  683.             if (!isset($params['report_type_id'])) {
  684.                 return $this->json(['success' => false'message' =>  $this->translator->trans('missing_required_parameters')]);
  685.             }
  686.             $reportTypeId $params['report_type_id'];
  687.             $parameters = new \Pimcore\Model\DataObject\ReportWeatherParameters\Listing();
  688.             $parameters->setCondition("reportType REGEXP CONCAT('(^|,)', REPLACE('" $reportTypeId "', ',', '|'), '(,|$)')");
  689.             $parameters->load();
  690.             if ($parameters->getCount() > 0) {
  691.                 foreach ($parameters as $paramter) {
  692.                     $result[] = [
  693.                         "id" => $paramter->getId(),
  694.                         "nameEn" => $paramter->getName('en'),
  695.                         "nameAr" => $paramter->getName('ar'),
  696.                         "meteoMaticsKey" => $paramter->getMeteoMaticsKey(),
  697.                         "units" => $paramter->getUnits(),
  698.                         "unitTitle" => $paramter->getUnitTitle()
  699.                     ];
  700.                 }
  701.                 return $this->json(["success" => true"data" => $result]);
  702.             }
  703.             return $this->json(["success" => false"message" => $this->translator->trans("no_weather_parameter_is_available")]);
  704.         } catch (\Exception $ex) {
  705.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  706.         }
  707.     }
  708.     /**
  709.      * @Route("/api/public/generate-excel", name="public-generate-excel")
  710.      */
  711.     public function generateExcelReport(Request $request)
  712.     {
  713.         $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  714.         if ($permissions['success'] !== true) {
  715.             return $this->json($permissions);
  716.         }
  717.         $user $permissions['user'];
  718.         $params  json_decode($request->getContent(), true);
  719.         $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  720.         if (!isset($params['id'])) {
  721.             throw new \Exception('missing_required_parameters');
  722.         }
  723.         $report Report::getById($params['id'], true);
  724.         if (!$report instanceof Report) {
  725.             throw new \Exception('no_report_found');
  726.         }
  727.         // Replace this with the actual data you want to use
  728.         $jsonData json_decode($report->getJsonData(), true);
  729.         // Create a new PhpSpreadsheet instance
  730.         $spreadsheet = new Spreadsheet();
  731.         // Create a worksheet
  732.         $sheet $spreadsheet->getActiveSheet();
  733.         // Determine headers dynamically based on the first item in the data
  734.         $firstItem reset($jsonData);
  735.         $parameters $firstItem['parameters'] ?? [];
  736.         $headers = ['City''Lat''Lon''Date']; // Initialize headers with common columns
  737.         // Extract parameter names and add them to headers
  738.         foreach ($parameters as $parameter) {
  739.             $headers[] = $parameter['parameter'];
  740.         }
  741.         // Add headers to the worksheet
  742.         foreach ($headers as $index => $header) {
  743.             $sheet->setCellValueByColumnAndRow($index 11$header);
  744.         }
  745.         // Initialize row counter
  746.         $row 2;
  747.         foreach ($jsonData as $item) {
  748.             // Extract city-related data
  749.             $cityData = [$item['city'] ?? ''$item['lat'] ?? ''$item['lon'] ?? ''];
  750.             // Loop through each date for all parameters
  751.             foreach ($item['parameters'][0]['dates'] as $dateIndex => $date) {
  752.                 // Initialize row data with city-related data and date
  753.                 $rowData array_merge($cityData, [date('Y-m-d'strtotime($date['date']))]);
  754.                 // Loop through each parameter
  755.                 foreach ($item['parameters'] as $parameter) {
  756.                     // Check if the value is set for the current date, otherwise set it to 0
  757.                     $value = isset($parameter['dates'][$dateIndex]['value']) ? $parameter['dates'][$dateIndex]['value'] : 0;
  758.                     // Add parameter value for the current date to the row data
  759.                     $rowData[] = $value;
  760.                 }
  761.                 // Set cell values explicitly
  762.                 foreach ($rowData as $colIndex => $cellValue) {
  763.                     $sheet->setCellValueByColumnAndRow($colIndex 1$row$cellValue);
  764.                 }
  765.                 // Increment the row counter
  766.                 $row++;
  767.             }
  768.         }
  769.         // Save the Excel file to var/export directory
  770.         $exportDir PIMCORE_PROJECT_ROOT '/var/export/';
  771.         if (!is_dir($exportDir)) {
  772.             @mkdir($exportDir0755true);
  773.         }
  774.         
  775.         $filename $user->getId() . '_weather_report';
  776.         $timestampedFilename $filename '_' time() . '.xlsx';
  777.         $exportPath $exportDir $timestampedFilename;
  778.         
  779.         $writer = new Xlsx($spreadsheet);
  780.         $writer->save($exportPath);
  781.         
  782.         // Return download URL using download-excel.php
  783.         $baseUrl $_ENV['BASE_URL_DEMO_AJWAA_API'] ?? API_BASE_URL ?? '';
  784.         $downloadUrl rtrim($baseUrl'/') . '/download-excel.php?file=' urlencode($timestampedFilename);
  785.         
  786.         return $this->json(['success' => true'data' => $downloadUrl]);
  787.     }
  788.     /**
  789.      * @Route("/api/public/generate-historical-report", methods={"POST"})
  790.      */
  791.     public function generateHistoricalReport(Request $request): JsonResponse
  792.     {
  793.         try {
  794.             $response = [];
  795.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  796.             if ($permissions['success'] !== true) {
  797.                 return $this->json($permissions);
  798.             }
  799.             $params json_decode($request->getContent(), true);
  800.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  801.             if (
  802.                 !isset($params['from_date']) ||
  803.                 !isset($params['to_date']) ||
  804.                 !isset($params['hours']) ||
  805.                 !isset($params['parameters'])  ||
  806.                 !isset($params['locations'])
  807.             ) {
  808.                 throw new \Exception('Missing required parameters');
  809.             }
  810.             if (empty($params['parameters'] || !is_array($params['parameters']))) {
  811.                 throw new \Exception('Parameters should be non empty array');
  812.             }
  813.             if (empty($params['locations'] || !is_array($params['locations']))) {
  814.                 throw new \Exception('Locations should be non empty array');
  815.             }
  816.             if ($params['to_date'] > date('Y-m-d'strtotime(Carbon::now()))) {
  817.                 $daysadd date('Y-m-d'strtotime(Carbon::now()->addDays('17')));
  818.                 if ($params['to_date'] >= $daysadd) {
  819.                     throw new \Exception('End date limit exceeded');
  820.                 }
  821.             }
  822.             $model = isset($params['model']) ? $params['model'] : 'mix';
  823.             $redisKey md5('generate_city_report-' $params['from_date'] . '-' $params['to_date'] . '-' $params['hours'] . '-' implode('_'$params['locations'])) . '-' implode('_'$params['parameters']) . '-' $model;
  824.             $data $this->redisCache->get($redisKey);
  825.             if (!$data) {
  826.                 $cities = new \Pimcore\Model\DataObject\Location\Listing();
  827.                 if (!empty($params['locations'])) {
  828.                     $cities->setCondition('o_id IN (?)', [$params['locations']]);
  829.                 }
  830.                 $cities->load();
  831.                 if ($cities->getCount() > 0) {
  832.                     $params['coordinates'] = []; // Initialize an empty array for coordinates
  833.                     $result = [];
  834.                     $citiesArr = [];
  835.                     foreach ($cities as $city) {
  836.                         $cityLocations json_decode($city->getCoordinates(), true);
  837.                         foreach ($cityLocations as $coordinates) {
  838.                             $long number_format($coordinates[1], 6'.''');
  839.                             $lat number_format($coordinates[0], 6'.''');
  840.                             $params['coordinates'][] = $coordinates;
  841.                             $citiesArr[$lat '|' $long] =  $city->getName();
  842.                         }
  843.                     }
  844.                     $result $this->meteomaticsWeatherService->getReportForecastData($params['coordinates'], $params['from_date'], $params['to_date'], $params['hours'], $model$params['parameters'], $this->translator$citiesArr$params);
  845.                     $response[] = $result;
  846.                     $jsonResponse = ['success' => true'data' => $response];
  847.                     $this->redisCache->set($redisKey$jsonResponseREDIS_CACHE_TIME);
  848.                     return $this->json($jsonResponse);
  849.                 } else {
  850.                     return $this->json(['success' => true'message' => $this->translator->trans('no_city_found')]);
  851.                 }
  852.             } else {
  853.                 return $this->json($data);
  854.             }
  855.         } catch (\Exception $ex) {
  856.             $this->logger->error($ex->getMessage());
  857.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  858.         }
  859.     }
  860.     /**
  861.      * @Route("/api/public/get-latest-report", name="public-get-latest-report")
  862.      */
  863.     public function getLatestReport(Request $request)
  864.     {
  865.         try {
  866.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  867.             if ($permissions['success'] !== true) {
  868.                 return $this->json($permissions);
  869.             }
  870.             $params  json_decode($request->getContent(), true);
  871.             $reportType = (isset($params['report_type']) && !empty($params['report_type'])) ? $params['report_type'] : 'ten-day-forecast-report';
  872.             $result $this->reportModel->getLatestReport($reportType$this->translatorfalse);
  873.             return $this->json($result);
  874.         } catch (\Exception $ex) {
  875.             $this->logger->error($ex->getMessage());
  876.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  877.         }
  878.     }
  879.     /**
  880.      * @Route("/api/public/get-automatic-reports", name="public_get_automatic_reports")
  881.      */
  882.     public function getAutomaticReports(Request $requestPaginatorInterface $paginator)
  883.     {
  884.         try {
  885.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  886.             if ($permissions['success'] !== true) {
  887.                 return $this->json($permissions);
  888.             }
  889.             $user $permissions['user'];
  890.             $params  json_decode($request->getContent(), true);
  891.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  892.             if (!isset($params['page']) || !isset($params['limit']) || !isset($params['lang'])) {
  893.                 throw new \Exception('Missing required parameters');
  894.             }
  895.             $search = isset($params['search']) ? $params['search'] : null;
  896.             $orderKey = isset($params['orderKey']) ? $params['orderKey'] : 'createdOn';
  897.             $order = isset($params['order']) ? $params['order'] : 'desc';
  898.             $result $this->reportModel->listAutomaticReports($params$user$search$orderKey$order$this->translator$paginator);
  899.             return $this->json($result);
  900.         } catch (\Exception $ex) {
  901.             $this->logger->error($ex->getMessage());
  902.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  903.         }
  904.     }
  905.     /**
  906.      * @Route("/api/public/generate-automatic-report-pdf", name="public-automatic-report-pdf")
  907.      */
  908.     public function generateAutomaticReportPdf(Request $request)
  909.     {
  910.         try {
  911.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  912.             if ($permissions['success'] !== true) {
  913.                 return $this->json($permissions);
  914.             }
  915.             $user $permissions['user'];
  916.             $result $this->reportModel->generatePdfReport($request$user$this->snappy$this->translator);
  917.             return  $this->json($result);
  918.         } catch (\Exception $ex) {
  919.             $this->logger->error($ex->getMessage());
  920.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  921.         }
  922.     }
  923.     public function getPdfReport($request$user$template$translator$report_type)
  924.     {
  925.         $params  json_decode($request->getContent(), true);
  926.         if (!isset($params['id'])) {
  927.             return $this->json(["success" => false"message" => $translator->trans("missing_required_parameters")]);
  928.         }
  929.         $report Report::getById($params['id'], true);
  930.         $lang = isset($params['lang']) ? $params['lang'] : "en";
  931.         if (!$report instanceof Report) {
  932.             return $this->json(["success" => false"message" => $translator->trans("no_report_found")]);
  933.         }
  934.         $asset $lang == 'ar' $report->getAssetAr() : $report->getAsset();
  935.         if ($asset) {
  936.             $pdfasset API_BASE_URL $asset->getPath() . $asset->getFilename();
  937.             return $this->json(['success' => true'data' => $pdfasset]);
  938.         }
  939.         //$template=$report->getReportType()?->getKey() == 'MannForecastReport'?'pdf/report_pdf_template.html.twig':'pdf/automatic_report_pdf_template.html.twig';
  940.         $fileName '_custom_weather_report.pdf';
  941.         $reportPath '/report/ReportPdf';
  942.         if ($report->getReportType()?->getKey() == 'MannForecastReport') {
  943.             $template =  'pdf/report_pdf_template.html.twig';
  944.         } elseif ($report->getReportType()?->getKey() == 'advance-custom-weather-report') {
  945.             $template 'pdf/advance_custom_report_pdf_template.html.twig';
  946.             $fileName '_advance_custom_weather_report.pdf';
  947.             $reportPath '/report/advanceCustomReportPdf';
  948.         } else {
  949.             $template 'pdf/automatic_report_pdf_template.html.twig';
  950.         }
  951.         $parameter = [
  952.             'data' => $report,
  953.             'reportTitleEn' => $report->getReportTitle('en'),
  954.             'reportTitleAr' => $report->getReportTitle('ar'),
  955.             'reportDescriptionEn' => $report->getDescription('en'),
  956.             'reportDescriptionAr' => $report->getDescription('ar'),
  957.             'reportDisclaimerEn' => $report->getReportDisclaimer('en'), // new 
  958.             'reportDisclaimerAr' => $report->getReportDisclaimer('ar'), // new 
  959.             'additionalNoteEn' => $report->getAdditionalNote('en'), // new 
  960.             'additionalNoteAr' => $report->getAdditionalNote('ar'), // new 
  961.             'template' => $template,
  962.             'lang' => $lang
  963.         ];
  964.         // return $this->render('pdf/automatic_report_pdf_template_copy.html.twig',$parameter);
  965.         $pdf \App\Lib\Utility::generatePdf($parameter$this->snappy);
  966.         // $tempFilePath = tempnam(sys_get_temp_dir(), 'image_');
  967.         // file_put_contents($tempFilePath, $pdf);
  968.         // Create a BinaryFileResponse and set headers
  969.         //    $response = new BinaryFileResponse($tempFilePath);
  970.         //    $response->headers->set('Content-Type', 'application/pdf');
  971.         //    $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE, 'image.pdf');
  972.         // Return the response
  973.         //    return $response;
  974.         $asset \App\Lib\Utility::createAsset($pdf$user->getId() . '_' time() . $report_type,  $reportPath);
  975.         $pdfasset '';
  976.         if ($asset instanceof Asset) {
  977.             $pdfasset API_BASE_URL $asset->getPath() . $asset->getFilename();
  978.         }
  979.         if ($lang == 'ar') {
  980.             $report->setAssetAr($asset);
  981.         } else {
  982.             $report->setAsset($asset);
  983.         }
  984.         $report->save();
  985.         return $this->json(['success' => true'data' => $pdfasset]);
  986.     }
  987.     /**
  988.      * @Route("/api/public/get-today-weather-report", name="public_get_today_weather_report")
  989.      */
  990.     public function getTodayWeatherReports(Request $requestPaginatorInterface $paginator)
  991.     {
  992.         try {
  993.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  994.             if ($permissions['success'] !== true) {
  995.                 return $this->json($permissions);
  996.             }
  997.             $user $permissions['user'];
  998.             $params  json_decode($request->getContent(), true);
  999.             if (!isset($params['page']) || !isset($params['limit']) || !isset($params['lang'])) {
  1000.                 throw new \Exception('Missing required parameters');
  1001.             }
  1002.             $result $this->reportModel->getTodayWeatherReports($params$this->translator$paginator);
  1003.             return $this->json($result);
  1004.         } catch (\Exception $ex) {
  1005.             $this->logger->error($ex->getMessage());
  1006.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1007.         }
  1008.     }
  1009.     /**
  1010.      * @Route("/api/public/get-report-weather-symbols", name="public_get_report_weather_symbols")
  1011.      */
  1012.     public function getReportWeatherSymbols(Request $request): JsonResponse
  1013.     {
  1014.         try {
  1015.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1016.             if ($permissions['success'] !== true) {
  1017.                 return $this->json($permissions);
  1018.             }
  1019.             $user $permissions['user'];
  1020.             $data = [];
  1021.             $weatherSymbols = new ReportWeatherSymbols\Listing();
  1022.             foreach ($weatherSymbols as $weatherSymbol) {
  1023.                 if ($weatherSymbol) {
  1024.                     $weatherSymbolNames $weatherSymbol->getWeatherSymbols();
  1025.                     $weatherSymbolIcons $weatherSymbol->getWeatherIcons();
  1026.                     if ($weatherSymbolNames) {
  1027.                         foreach ($weatherSymbolNames as $weatherSymbolName) {
  1028.                             $response['symbols'][] = [
  1029.                                 'nameEn' => $weatherSymbolName->getSymbolName('en'),
  1030.                                 'nameAr' => $weatherSymbolName->getSymbolName('ar'),
  1031.                             ];
  1032.                         }
  1033.                     }
  1034.                     if ($weatherSymbolIcons) {
  1035.                         foreach ($weatherSymbolIcons as $weatherSymbolIcon) {
  1036.                             $response['icons'][] = [
  1037.                                 'iconValue' => $weatherSymbolIcon->getIconValue(),
  1038.                             ];
  1039.                         }
  1040.                     }
  1041.                 }
  1042.                 $data[] = $response;
  1043.             }
  1044.             return $this->json(['success' => true'data' => $data]);
  1045.         } catch (\Exception $ex) {
  1046.             $this->logger->error($ex->getMessage());
  1047.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  1048.         }
  1049.     }
  1050.     /**
  1051.      * @Route("/api/public/get-forecast-cities", methods={"POST"})
  1052.      * @Route("/api/public/get-cities", name="get-wso-cities")
  1053.      */
  1054.     public function getForecastCities(Request $request): Response
  1055.     {
  1056.         try {
  1057.             $params json_decode($request->getContent(), true);
  1058.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1059.             // check user credentials and expiry
  1060.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1061.             if ($response['success'] !== true) {
  1062.                 return $this->json($response);
  1063.             }
  1064.             $forecastCityModel = new WeatherForecastCityModel();
  1065.             $cities $forecastCityModel->getWeatherForecastCities($params);
  1066.             return $this->json(['success' => true'data' => $cities]);
  1067.         } catch (\Exception $ex) {
  1068.             $this->logger->error($ex->getMessage());
  1069.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1070.         }
  1071.     }
  1072.     /**
  1073.      * @Route("/api/public/get-regions-bbox", name="get-regions-bbox")
  1074.      */
  1075.     public function getRegionsBbox(Request $request)
  1076.     {
  1077.         try {
  1078.             $params json_decode($request->getContent(), true);
  1079.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1080.             // check user credentials and expiry
  1081.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1082.             if ($response['success'] !== true) {
  1083.                 return $this->json($response);
  1084.             }
  1085.             $data REGIONS_BBOX;
  1086.             return $this->json(['success' => true'data' => $data]);
  1087.         } catch (\Exception $ex) {
  1088.             $this->logger->error($ex->getMessage());
  1089.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1090.         }
  1091.     }
  1092.     /**
  1093.      * @Route("/api/public/get-station-data", methods={"GET"})
  1094.      */
  1095.     public function getWeatherStationDataAction(Request $request)
  1096.     {
  1097.         try {
  1098.             $params json_decode($request->getContent(), true);
  1099.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1100.             // check user credentials and expiry
  1101.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1102.             if ($response['success'] !== true) {
  1103.                 return $this->json($response);
  1104.             }
  1105.             $typeName $request->get('type_name');
  1106.             $parameters $request->get('parameters');
  1107.             $dateTime $request->get('date_time');
  1108.             $bBox $request->get('b_box');
  1109.             if (!$typeName) {
  1110.                 throw new \InvalidArgumentException("Missing mandatory parameter: type_name");
  1111.             }
  1112.             if (!$parameters) {
  1113.                 throw new \InvalidArgumentException("Missing mandatory parameter: parameters");
  1114.             }
  1115.             if (!$dateTime) {
  1116.                 throw new \InvalidArgumentException("Missing mandatory parameter: date_time");
  1117.             }
  1118.             if (!$bBox) {
  1119.                 throw new \InvalidArgumentException("Missing mandatory parameter: b_box");
  1120.             }
  1121.             $result $this->meteomaticsWeatherService->getWeatherStationData($typeName$parameters$dateTime$bBox);
  1122.             return $result;
  1123.         } catch (\Exception $ex) {
  1124.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1125.         }
  1126.     }
  1127.     /**
  1128.      * @Route("/api/public/daily-forecast", methods={"POST"})
  1129.      */
  1130.     public function dailyForecast(Request $request): JsonResponse
  1131.     {
  1132.         try {
  1133.             $params json_decode($request->getContent(), true);
  1134.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1135.             // check user credentials and expiry
  1136.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1137.             if ($response['success'] !== true) {
  1138.                 return $this->json($response);
  1139.             }
  1140.             $requiredParameters = ['coordinates''from_date''to_date''model'];
  1141.             foreach ($requiredParameters as $param) {
  1142.                 if (!isset($params[$param])) {
  1143.                     $missingParams[] = $param;
  1144.                 }
  1145.             }
  1146.             if (!empty($missingParams)) {
  1147.                 // Throw an exception with a message that includes the missing parameters
  1148.                 $parameterList implode(", "$missingParams);
  1149.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1150.             }
  1151.             $result $this->meteomaticsWeatherService->getForecastData($params['coordinates'], $params['from_date'], $params['to_date'], $params['hours'], $params['model'], $this->translator);
  1152.             return $this->json($result);
  1153.         } catch (\Exception $ex) {
  1154.             $this->logger->error($ex->getMessage());
  1155.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  1156.         }
  1157.     }
  1158.     /**
  1159.      * @Route("/api/public/weather/{path}", name="api_meteomatics", requirements={"path"=".+"}, methods={"GET"})
  1160.      */
  1161.     public function getDynamicWeatherData(Request $requeststring $path): Response
  1162.     {
  1163.         try {
  1164.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1165.             if ($response['success'] !== true) {
  1166.                 return $this->json($response);
  1167.             }
  1168.             $user $response['user'];
  1169.             $queryParams $request->query->all();
  1170.             $weatherData $this->meteomaticApiService->getDynamicWeatherData($path$queryParams$user);
  1171.             return $weatherData;
  1172.         } catch (\Exception $ex) {
  1173.             $this->logger->error($ex->getMessage());
  1174.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  1175.         }
  1176.     }
  1177.     /**
  1178.      * @Route("/api/public/create-manned-alert-subscription", methods={"POST"})
  1179.      */
  1180.     public function mannedAlertSubscription(Request $request): JsonResponse
  1181.     {
  1182.         try {
  1183.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1184.             if ($response['success'] !== true) {
  1185.                 return $this->json($response);
  1186.             }
  1187.             $user $response['user'];
  1188.             $params json_decode($request->getContent(), true);
  1189.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1190.             // $requiredParameters = ['region_id', 'governorate_id', 'alert_type_id'];
  1191.             // foreach ($requiredParameters as $param) {
  1192.             //     if (!isset($params[$param]) || empty($params[$param])) {
  1193.             //         $missingParams[] = $param;
  1194.             //     }
  1195.             // }
  1196.             // if (!empty($missingParams)) {
  1197.             //     // Throw an exception with a message that includes the missing parameters
  1198.             //     $parameterList = implode(", ", $missingParams);
  1199.             //     return $this->json(['success' => false, 'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1200.             // }
  1201.             $result $this->customNotificationService->mannedAlertSubscription($user$params$this->translator);
  1202.             return $this->json($result);
  1203.         } catch (\Exception $ex) {
  1204.             $this->logger->error($ex->getMessage());
  1205.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  1206.         }
  1207.     }
  1208.     /**
  1209.      * @Route("/api/public/upload-bulk-locations", methods={"POST"})
  1210.      */
  1211.     public function uploadBulkLocations(Request $request): JsonResponse
  1212.     {
  1213.         try {
  1214.             $lang $request->request->get('lang');
  1215.             $email $request->request->get('email''umer.wasi@centricdxb.com');
  1216.             $name $request->request->get('name''User');
  1217.             $reason $request->request->get('request_reason''N/A');
  1218.             $file $request->files->get('file');
  1219.             $this->translator->setlocale(isset($lang) ? $lang DEFAULT_LOCALE);
  1220.             if (!$file || !$file->isValid()) {
  1221.                 return new JsonResponse(['success' => false'message' => 'Invalid or missing file'], 400);
  1222.             }
  1223.             // Upload file to c2service and get asset ID
  1224.             $assetId $this->c2Service->uploadToC2service($file);
  1225.             if (!$assetId) {
  1226.                 throw new \Exception('Failed to upload to c2service');
  1227.             }
  1228.             $html $this->templating->render('web2print/_request_location.html.twig', ['name' => $name'reason' => $reason]);
  1229.             $result $this->c2Service->sendWeatherDashboardEmail($_ENV['WEATHER_DASHBOARD_LOCATIONS'], $email$html''$assetId);
  1230.             return $this->json(['success' => true'message' => $this->translator->trans('file_successfully_submitted')]);
  1231.         } catch (\Exception $ex) {
  1232.             $this->logger->error($ex->getMessage());
  1233.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  1234.         }
  1235.     }
  1236.     /**
  1237.      * @Route("/api/public/get-token", methods={"POST"})
  1238.      */
  1239.     public function getToken(Request $request): JsonResponse
  1240.     {
  1241.         try {
  1242.             $result $this->meteomaticApiService->getToken();
  1243.             return $this->json($result);
  1244.         } catch (\Exception $ex) {
  1245.             $this->logger->error($ex->getMessage());
  1246.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1247.         }
  1248.     }
  1249.     /**
  1250.      * @Route("/api/public/weather/get-solar-power", methods={"POST"})
  1251.      */
  1252.     public function getSolarPowerAction(Request $request)
  1253.     {
  1254.         try {
  1255.             
  1256.             $params json_decode($request->getContent(), true);
  1257.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1258.             // check user credentials and expiry// check user credentials and expiry
  1259.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1260.             if ($response['success'] !== true) {
  1261.                 return $this->json($response);
  1262.             }
  1263.             $mandatoryParams = ['latitude''longitude''hour''specification''interval_in_hours''start_date''end_date''unit'];
  1264.             foreach ($mandatoryParams as $param) {
  1265.                 if (!isset($params[$param]) || empty($params[$param])) {
  1266.                     $missingParams[] = $param;
  1267.                 }
  1268.             }
  1269.             if (!empty($missingParams)) {
  1270.                 // Throw an exception with a message that includes the missing parameters
  1271.                 $parameterList implode(", "$missingParams);
  1272.                 throw new \InvalidArgumentException(sprintf($this->translator->trans("missing_or_empty_mandatory_parameter: %s"), $parameterList));
  1273.             }
  1274.             // Extract the necessary parameters for getWeatherWarnings function  
  1275.             $coordinates = [[$params['latitude'], $params['longitude']]];
  1276.             $startDate $params['start_date'];
  1277.             $endDate $params['end_date'];
  1278.             $specification $params['specification'];
  1279.             $intervalInHours $params['interval_in_hours'];
  1280.             $hour $params['hour'];
  1281.             $unit $params['unit'];
  1282.             // Call the getWindPower function with validated parameters
  1283.             $result $this->meteomaticsWeatherService->getSolarPower($coordinates$startDate$endDate$intervalInHours$hour$unit$specification$this->translator);
  1284.             return $this->json($result);
  1285.         } catch (\Exception $ex) {
  1286.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1287.         }
  1288.     }
  1289.     /**
  1290.      * @Route("/api/public/weather/air-density", methods={"POST"})
  1291.      */
  1292.     public function airdensityData(Request $request): JsonResponse
  1293.     {
  1294.         try {
  1295.             // check user credentials and expiry
  1296.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1297.             if ($response['success'] !== true) {
  1298.                 return $this->json($response);
  1299.             }
  1300.             $params json_decode($request->getContent(), true);
  1301.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1302.             $requiredParameters = ['hour''start_date''end_date''coordinates''level''unit''format'];
  1303.             foreach ($requiredParameters as $param) {
  1304.                 if (!isset($params[$param])) {
  1305.                     $missingParams[] = $param;
  1306.                 }
  1307.             }
  1308.             if (!empty($missingParams)) {
  1309.                 // Throw an exception with a message that includes the missing parameters
  1310.                 $parameterList implode(", "$missingParams);
  1311.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1312.             }
  1313.             $hour $params['hour'];
  1314.             $startDate $params['start_date'];
  1315.             $endDate $params['end_date'];
  1316.             $coordinates $params['coordinates'];
  1317.             $level $params['level'];
  1318.             $unit $params['unit'];
  1319.             $format $params['format'];
  1320.             $response $this->meteomaticsWeatherService->getAirdensity(
  1321.                 $hour,
  1322.                 $startDate,
  1323.                 $endDate,
  1324.                 $coordinates,
  1325.                 $level,
  1326.                 $unit,
  1327.                 $format
  1328.             );
  1329.             // You can return or process the $data as needed
  1330.             // For demonstration purposes, we will just return a JSON response
  1331.             return $this->json($response);
  1332.         } catch (\Exception $ex) {
  1333.             $this->logger->error($ex->getMessage());
  1334.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1335.         }
  1336.     }
  1337.     /**
  1338.      * @Route("/api/public/weather/get-metar-data", methods={"POST"})
  1339.      */
  1340.     public function getMetarData(Request $request): JsonResponse
  1341.     {
  1342.         try {
  1343.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1344.             if ($response['success'] !== true) {
  1345.                 return $this->json($response);
  1346.             }
  1347.             $params json_decode($request->getContent(), true);
  1348.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1349.             $requiredParameters = ['startDate''endDate''metar''duration'];
  1350.             foreach ($requiredParameters as $param) {
  1351.                 if (!isset($params[$param])) {
  1352.                     $missingParams[] = $param;
  1353.                 }
  1354.             }
  1355.             if (!empty($missingParams)) {
  1356.                 // Throw an exception with a message that includes the missing parameters
  1357.                 $parameterList implode(", "$missingParams);
  1358.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1359.             }
  1360.             $startDate $params['startDate'];
  1361.             $endDate $params['endDate'];
  1362.             $metar $params['metar'];
  1363.             $duration $params['duration'];
  1364.             $parameters $params['parameter'] ?? null;
  1365.             $genExcel $request->get('gen_excel'false);
  1366.             $metarData $this->meteomaticsWeatherService->getMetarData($startDate$endDate$metar$duration$this->translator$parameters$genExcel);
  1367.             return $this->json(['success' => true'message' => $this->translator->trans("excel_file_downloaded_successfully"), 'data' => $metarData]);
  1368.         } catch (\Exception $ex) {
  1369.             $this->logger->error($ex->getMessage());
  1370.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1371.         }
  1372.     }
  1373.     /**
  1374.      * @Route("/api/public/weather/frost-thaw-depth", methods={"POST"})
  1375.      */
  1376.     public function frostThawAndDepthData(Request $request): JsonResponse
  1377.     {
  1378.         try {
  1379.             // check user credentials and expiry
  1380.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1381.             if ($response['success'] !== true) {
  1382.                 return $this->json($response);
  1383.             }
  1384.             $params json_decode($request->getContent(), true);
  1385.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1386.             $requiredParameters = ['hour''start_date''end_date''unit''coordinates''format'];
  1387.             foreach ($requiredParameters as $param) {
  1388.                 if (!isset($params[$param])) {
  1389.                     $missingParams[] = $param;
  1390.                 }
  1391.             }
  1392.             if (!empty($missingParams)) {
  1393.                 // Throw an exception with a message that includes the missing parameters
  1394.                 $parameterList implode(", "$missingParams);
  1395.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1396.             }
  1397.             $hour $params['hour'];
  1398.             $startDate $params['start_date'];
  1399.             $endDate $params['end_date'];
  1400.             $unit $params['unit'];
  1401.             $coordinates $params['coordinates'];
  1402.             $format $params['format'];
  1403.             $response $this->meteomaticsWeatherService->getFrostThawAndDepth(
  1404.                 $hour,
  1405.                 $startDate,
  1406.                 $endDate,
  1407.                 $unit,
  1408.                 $coordinates,
  1409.                 $format
  1410.             );
  1411.             // You can return or process the $data as needed
  1412.             // For demonstration purposes, we will just return a JSON response
  1413.             return $this->json($response);
  1414.         } catch (\Exception $ex) {
  1415.             $this->logger->error($ex->getMessage());
  1416.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1417.         }
  1418.     }
  1419.     /**
  1420.      * @Route("/api/public/weather/get-hail-index", methods={"POST"})
  1421.      */
  1422.     public function getHailIndexAction(Request $request): JsonResponse
  1423.     {
  1424.         try {
  1425.             // check user credentials and expiry
  1426.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1427.             if ($response['success'] !== true) {
  1428.                 return $this->json($response);
  1429.             }
  1430.             $params json_decode($request->getContent(), true);
  1431.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1432.             $mandatoryParams = ['coordinates''start_date''end_date''duration''interval_type'];
  1433.             foreach ($mandatoryParams as $param) {
  1434.                 if (!isset($params[$param]) || empty($params[$param])) {
  1435.                     $missingParams[] = $param;
  1436.                 }
  1437.             }
  1438.             if (!empty($missingParams)) {
  1439.                 // Throw an exception with a message that includes the missing parameters
  1440.                 $parameterList implode(", "$missingParams);
  1441.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1442.             }
  1443.             // Extract the necessary parameters for getWeatherWarnings function
  1444.             $coordinates $params['coordinates'];
  1445.             $startDate $params['start_date'];
  1446.             $endDate $params['end_date'];
  1447.             $intervalType $params['interval_type'];
  1448.             $duration $params['duration'] ?? '5';
  1449.             $format $params['format'] ?? 'json';
  1450.             // Call the getWeatherWarnings function with validated parameters
  1451.             $result $this->meteomaticsWeatherService->getHailIndex($coordinates$startDate$endDate$duration$intervalType$format$this->translator);
  1452.             return $this->json($result);
  1453.         } catch (\Exception $ex) {
  1454.             $this->logger->error($ex->getMessage());
  1455.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  1456.         }
  1457.     }
  1458.     /**
  1459.      * @Route("/api/public/weather/soil-moisture-index", methods={"POST"})
  1460.      */
  1461.     public function soilMoistureIndexData(Request $request): JsonResponse
  1462.     {
  1463.         try {
  1464.             // check user credentials and expiry
  1465.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1466.             if ($response['success'] !== true) {
  1467.                 return $this->json($response);
  1468.             }
  1469.             $params json_decode($request->getContent(), true);
  1470.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1471.             $requiredParameters = ['hour''start_date''end_date''coordinates''level''unit''format'];
  1472.             foreach ($requiredParameters as $param) {
  1473.                 if (!isset($params[$param])) {
  1474.                     $missingParams[] = $param;
  1475.                 }
  1476.             }
  1477.             if (!empty($missingParams)) {
  1478.                 // Throw an exception with a message that includes the missing parameters
  1479.                 $parameterList implode(", "$missingParams);
  1480.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1481.             }
  1482.             $hour $params['hour'];
  1483.             $startDate $params['start_date'];
  1484.             $endDate $params['end_date'];
  1485.             $coordinates $params['coordinates'];
  1486.             $level $params['level'];
  1487.             $unit $params['unit'];
  1488.             $format $params['format'];
  1489.             $response $this->meteomaticsWeatherService->getSoilMoistureIndex(
  1490.                 $hour,
  1491.                 $startDate,
  1492.                 $endDate,
  1493.                 $coordinates,
  1494.                 $level,
  1495.                 $unit,
  1496.                 $format
  1497.             );
  1498.             // You can return or process the $data as needed
  1499.             // For demonstration purposes, we will just return a JSON response
  1500.             return $this->json($response);
  1501.         } catch (\Exception $ex) {
  1502.             $this->logger->error($ex->getMessage());
  1503.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1504.         }
  1505.     }
  1506.     /**
  1507.      * @Route("/api/public/weather/get-moving-average", methods={"POST"})
  1508.      */
  1509.     public function getMovingAverage(Request $request): JsonResponse
  1510.     {
  1511.         try {
  1512.             // check user credentials and expiry
  1513.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1514.             if ($response['success'] !== true) {
  1515.                 return $this->json($response);
  1516.             }
  1517.             $params  json_decode($request->getContent(), true);
  1518.             $requiredParameters = ['start_from''days''location'"name"];
  1519.             foreach ($requiredParameters as $param) {
  1520.                 if (!isset($params[$param])) {
  1521.                     $missingParams[] = $param;
  1522.                 }
  1523.             }
  1524.             if (!empty($missingParams)) {
  1525.                 $parameterList implode(", "$missingParams);
  1526.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1527.             }
  1528.             switch ($params['name']) {
  1529.                 case 'temperature_day':
  1530.                     $data $this->insuranceIndustryService->mmTemperatureDay($params['start_from'], $params['days'], $params['location']);
  1531.                     break;
  1532.                 case 'temperature_night':
  1533.                     $data $this->insuranceIndustryService->mmTemperatureNight($params['start_from'], $params['days'], $params['location']);
  1534.                     break;
  1535.                 case 'wind_speed':
  1536.                     $data $this->insuranceIndustryService->mmWindSpeed($params['start_from'], $params['days'], $params['location']);
  1537.                     break;
  1538.                 case 'soil_water_content':
  1539.                     $data $this->insuranceIndustryService->mmSoilWaterContent($params['start_from'], $params['days'], $params['location']);
  1540.                     break;
  1541.                 default:
  1542.                     // Handle invalid function name
  1543.                     return $this->json(['success' => false'message' => 'Invalid name']);
  1544.             }
  1545.             return $this->json(['success' => true'data' => $data]);
  1546.         } catch (\Exception $ex) {
  1547.             $this->logger->error($ex->getMessage());
  1548.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  1549.         }
  1550.     }
  1551.     /**
  1552.      * @Route("/api/public/weather/heat-index", methods={"POST"})
  1553.      */
  1554.     public function heatIndexData(Request $request): JsonResponse
  1555.     {
  1556.         try {
  1557.             // check user credentials and expiry
  1558.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1559.             if ($response['success'] !== true) {
  1560.                 return $this->json($response);
  1561.             }
  1562.             $params json_decode($request->getContent(), true);
  1563.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1564.             $requiredParameters = ['hour''start_date''end_date''coordinates''unit''format''model'];
  1565.             foreach ($requiredParameters as $param) {
  1566.                 if (!isset($params[$param])) {
  1567.                     $missingParams[] = $param;
  1568.                 }
  1569.             }
  1570.             if (!empty($missingParams)) {
  1571.                 // Throw an exception with a message that includes the missing parameters
  1572.                 $parameterList implode(", "$missingParams);
  1573.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1574.             }
  1575.             $hour $params['hour'];
  1576.             $startDate $params['start_date'];
  1577.             $endDate $params['end_date'];
  1578.             $coordinates $params['coordinates'];
  1579.             $unit $params['unit'];
  1580.             $format $params['format'];
  1581.             $model $params['model'];
  1582.             $response $this->meteomaticsWeatherService->getHeatIndex(
  1583.                 $hour,
  1584.                 $startDate,
  1585.                 $endDate,
  1586.                 $coordinates,
  1587.                 $unit,
  1588.                 $format,
  1589.                 $model
  1590.             );
  1591.             // You can return or process the $data as needed
  1592.             // For demonstration purposes, we will just return a JSON response
  1593.             return $this->json($response);
  1594.         } catch (\Exception $ex) {
  1595.             $this->logger->error($ex->getMessage());
  1596.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1597.         }
  1598.     }
  1599.     /**
  1600.      * @Route("/api/public/weather/get-seasonal-average", methods={"POST"})
  1601.      */
  1602.     public function getSeasonalAverager(Request $request): JsonResponse
  1603.     {
  1604.         try {
  1605.             // check user credentials and expiry
  1606.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1607.             if ($response['success'] !== true) {
  1608.                 return $this->json($response);
  1609.             }
  1610.             $params  json_decode($request->getContent(), true);
  1611.             $requiredParameters = ['date''day_time''parameter''location'];
  1612.             foreach ($requiredParameters as $param) {
  1613.                 if (!isset($params[$param])) {
  1614.                     $missingParams[] = $param;
  1615.                 }
  1616.             }
  1617.             if (!empty($missingParams)) {
  1618.                 $parameterList implode(", "$missingParams);
  1619.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1620.             }
  1621.             $data $this->insuranceIndustryService->seasonalAverage($params['date'], $params['day_time'], $params['parameter'], $params['location']);
  1622.             return $this->json(['success' => true'data' => $data]);
  1623.         } catch (\Exception $ex) {
  1624.             $this->logger->error($ex->getMessage());
  1625.             return $this->json(['success' => false$this->translator->trans(USER_ERROR_MESSAGE)]);
  1626.         }
  1627.     }
  1628.     /**
  1629.      * @Route("/api/public/weather/get-threats-table", methods={"POST"})
  1630.      */
  1631.     public function getThreatTable(Request $request): JsonResponse
  1632.     {
  1633.         try {
  1634.             // check user credentials and expiry
  1635.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1636.             if ($response['success'] !== true) {
  1637.                 return $this->json($response);
  1638.             }
  1639.             $params  json_decode($request->getContent(), true);
  1640.             $requiredParameters = ['date''day_deep''location'];
  1641.             foreach ($requiredParameters as $param) {
  1642.                 if (!isset($params[$param])) {
  1643.                     $missingParams[] = $param;
  1644.                 }
  1645.             }
  1646.             if (!empty($missingParams)) {
  1647.                 $parameterList implode(", "$missingParams);
  1648.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1649.             }
  1650.             $data $this->insuranceIndustryService->threatTable($params['date'], $params['day_deep'], $params['location']);
  1651.             return $this->json(['success' => true'data' => $data]);
  1652.         } catch (\Exception $ex) {
  1653.             $this->logger->error($ex->getMessage());
  1654.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  1655.         }
  1656.     }
  1657.     /**
  1658.      * @Route("/api/public/weather/high-low-tide-times", methods={"POST"})
  1659.      */
  1660.     public function highLowTideTimesData(Request $request): JsonResponse
  1661.     {
  1662.         try {
  1663.             // check user credentials and expiry
  1664.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1665.             if ($response['success'] !== true) {
  1666.                 return $this->json($response);
  1667.             }
  1668.             $params json_decode($request->getContent(), true);
  1669.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1670.             $requiredParameters = ['hour''start_date''end_date''coordinates''model''format'];
  1671.             foreach ($requiredParameters as $param) {
  1672.                 if (!isset($params[$param])) {
  1673.                     $missingParams[] = $param;
  1674.                 }
  1675.             }
  1676.             if (!empty($missingParams)) {
  1677.                 // Throw an exception with a message that includes the missing parameters
  1678.                 $parameterList implode(", "$missingParams);
  1679.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1680.             }
  1681.             $hour $params['hour'];
  1682.             $startDate $params['start_date'];
  1683.             $endDate $params['end_date'];
  1684.             $coordinates $params['coordinates'];
  1685.             $model $params['model'];
  1686.             $format $params['format'];
  1687.             $response $this->meteomaticsWeatherService->getHighLowTideTimes(
  1688.                 $hour,
  1689.                 $startDate,
  1690.                 $endDate,
  1691.                 $coordinates,
  1692.                 $model,
  1693.                 $format
  1694.             );
  1695.             // You can return or process the $data as needed
  1696.             // For demonstration purposes, we will just return a JSON response
  1697.             return $this->json($response);
  1698.         } catch (\Exception $ex) {
  1699.             $this->logger->error($ex->getMessage());
  1700.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1701.         }
  1702.     }
  1703.     /**
  1704.      * @Route("/api/public/weather/tidal-amplitude", methods={"POST"})
  1705.      */
  1706.     public function tidalAmplitudeData(Request $request): JsonResponse
  1707.     {
  1708.         try {
  1709.             // check user credentials and expiry
  1710.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1711.             if ($response['success'] !== true) {
  1712.                 return $this->json($response);
  1713.             }
  1714.             $params json_decode($request->getContent(), true);
  1715.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1716.             $requiredParameters = ['hour''start_date''end_date''coordinates''unit''model''format'];
  1717.             foreach ($requiredParameters as $param) {
  1718.                 if (!isset($params[$param])) {
  1719.                     $missingParams[] = $param;
  1720.                 }
  1721.             }
  1722.             if (!empty($missingParams)) {
  1723.                 // Throw an exception with a message that includes the missing parameters
  1724.                 $parameterList implode(", "$missingParams);
  1725.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1726.             }
  1727.             $hour $params['hour'];
  1728.             $startDate $params['start_date'];
  1729.             $endDate $params['end_date'];
  1730.             $coordinates $params['coordinates'];
  1731.             $unit $params['unit'];
  1732.             $model $params['model'];
  1733.             $format $params['format'];
  1734.             $response $this->meteomaticsWeatherService->getTidalAmplitudes(
  1735.                 $hour,
  1736.                 $startDate,
  1737.                 $endDate,
  1738.                 $coordinates,
  1739.                 $unit,
  1740.                 $model,
  1741.                 $format
  1742.             );
  1743.             // You can return or process the $data as needed
  1744.             // For demonstration purposes, we will just return a JSON response
  1745.             return $this->json($response);
  1746.         } catch (\Exception $ex) {
  1747.             $this->logger->error($ex->getMessage());
  1748.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1749.         }
  1750.     }
  1751.     /**
  1752.      * @Route("/api/public/weather/significant-wave-height", methods={"POST"})
  1753.      */
  1754.     public function significantWaveHeightData(Request $request): JsonResponse
  1755.     {
  1756.         try {
  1757.             // check user credentials and expiry
  1758.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1759.             if ($response['success'] !== true) {
  1760.                 return $this->json($response);
  1761.             }
  1762.             $params json_decode($request->getContent(), true);
  1763.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1764.             $requiredParameters = ['hour''start_date''end_date''coordinates''format'];
  1765.             foreach ($requiredParameters as $param) {
  1766.                 if (!isset($params[$param])) {
  1767.                     $missingParams[] = $param;
  1768.                 }
  1769.             }
  1770.             if (!empty($missingParams)) {
  1771.                 // Throw an exception with a message that includes the missing parameters
  1772.                 $parameterList implode(", "$missingParams);
  1773.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1774.             }
  1775.             $hour $params['hour'];
  1776.             $startDate $params['start_date'];
  1777.             $endDate $params['end_date'];
  1778.             $coordinates $params['coordinates'];
  1779.             $format $params['format'];
  1780.             $response $this->meteomaticsWeatherService->getSignificantWaveHeight(
  1781.                 $hour,
  1782.                 $startDate,
  1783.                 $endDate,
  1784.                 $coordinates,
  1785.                 $format
  1786.             );
  1787.             // You can return or process the $data as needed
  1788.             // For demonstration purposes, we will just return a JSON response
  1789.             return $this->json($response);
  1790.         } catch (\Exception $ex) {
  1791.             $this->logger->error($ex->getMessage());
  1792.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1793.         }
  1794.     }
  1795.     /**
  1796.      * @Route("/api/public/weather/surge-amplitude", methods={"POST"})
  1797.      */
  1798.     public function surgeAmplitudeData(Request $request): JsonResponse
  1799.     {
  1800.         try {
  1801.             // check user credentials and expiry
  1802.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1803.             if ($response['success'] !== true) {
  1804.                 return $this->json($response);
  1805.             }
  1806.             $params json_decode($request->getContent(), true);
  1807.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1808.             $requiredParameters = ['hour''start_date''end_date''coordinates''unit''model''format'];
  1809.             foreach ($requiredParameters as $param) {
  1810.                 if (!isset($params[$param])) {
  1811.                     $missingParams[] = $param;
  1812.                 }
  1813.             }
  1814.             if (!empty($missingParams)) {
  1815.                 // Throw an exception with a message that includes the missing parameters
  1816.                 $parameterList implode(", "$missingParams);
  1817.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1818.             }
  1819.             $hour $params['hour'];
  1820.             $startDate $params['start_date'];
  1821.             $endDate $params['end_date'];
  1822.             $coordinates $params['coordinates'];
  1823.             $unit $params['unit'];
  1824.             $model $params['model'];
  1825.             $format $params['format'];
  1826.             $response $this->meteomaticsWeatherService->getSurgeAmplitude(
  1827.                 $hour,
  1828.                 $startDate,
  1829.                 $endDate,
  1830.                 $coordinates,
  1831.                 $unit,
  1832.                 $model,
  1833.                 $format
  1834.             );
  1835.             // You can return or process the $data as needed
  1836.             // For demonstration purposes, we will just return a JSON response
  1837.             return $this->json($response);
  1838.         } catch (\Exception $ex) {
  1839.             $this->logger->error($ex->getMessage());
  1840.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1841.         }
  1842.     }
  1843.     /**
  1844.      * @Route("/api/public/weather/fog", methods={"POST"})
  1845.      */
  1846.     public function fogData(Request $request): JsonResponse
  1847.     {
  1848.         try {
  1849.             // check user credentials and expiry
  1850.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1851.             if ($response['success'] !== true) {
  1852.                 return $this->json($response);
  1853.             }
  1854.             $params json_decode($request->getContent(), true);
  1855.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1856.             $requiredParameters = ['start_date''end_date''coordinates''format'];
  1857.             foreach ($requiredParameters as $param) {
  1858.                 if (!isset($params[$param])) {
  1859.                     $missingParams[] = $param;
  1860.                 }
  1861.             }
  1862.             if (!empty($missingParams)) {
  1863.                 // Throw an exception with a message that includes the missing parameters
  1864.                 $parameterList implode(", "$missingParams);
  1865.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1866.             }
  1867.             $startDate $params['start_date'];
  1868.             $endDate $params['end_date'];
  1869.             $interval $params['interval'];
  1870.             $unit $params['unit'];
  1871.             $coordinates $params['coordinates'];
  1872.             $format $params['format'];
  1873.             $response $this->meteomaticsWeatherService->getFog(
  1874.                 $coordinates,
  1875.                 $startDate,
  1876.                 $endDate,
  1877.                 $interval,
  1878.                 $unit,
  1879.                 $this->translator,
  1880.                 $format
  1881.             );
  1882.             // You can return or process the $data as needed
  1883.             // For demonstration purposes, we will just return a JSON response
  1884.             return $this->json($response);
  1885.         } catch (\Exception $ex) {
  1886.             $this->logger->error($ex->getMessage());
  1887.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  1888.         }
  1889.     }
  1890.     /**
  1891.      * @Route("/api/public/weather/generic-api", methods={"POST"})
  1892.      */
  1893.     public function fetchMeteomaticData(Request $request): JsonResponse
  1894.     {
  1895.         $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1896.         if ($response['success'] !== true) {
  1897.             return $this->json($response);
  1898.         }
  1899.         $params json_decode($request->getContent(), true);
  1900.         $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1901.         if (!isset($params['format'])) {
  1902.             throw new \InvalidArgumentException('Missing "format" parameter');
  1903.         }
  1904.         if ($params['format'] == "json") {
  1905.             if (
  1906.                 !isset($params['startdate']) ||
  1907.                 !isset($params['enddate']) ||
  1908.                 !isset($params['resolution']) ||
  1909.                 !isset($params['parameters']) ||
  1910.                 !isset($params['lat']) ||
  1911.                 !isset($params['lon']) ||
  1912.                 !isset($params['format'])
  1913.             ) {
  1914.                 throw new \Exception('Missing required parameters');
  1915.             }
  1916.             // date_default_timezone_set('UTC');
  1917.             //$hour = $params['hour'];
  1918.             $format $params['format'];
  1919.             $startDate $params['startdate'];
  1920.             $endDate $params['enddate'];
  1921.             $resolution $params['resolution'];
  1922.             $parameters $params['parameters'];
  1923.             $model $params['model'];
  1924.             $lat $params['lat'];
  1925.             $lon $params['lon'];
  1926.             $response $this->meteomaticApiService->timeSeriesQuery(
  1927.                 $startDate,
  1928.                 $endDate,
  1929.                 $resolution,
  1930.                 $parameters,
  1931.                 $model,
  1932.                 $lat,
  1933.                 $lon,
  1934.                 $format,
  1935.                 $this->translator
  1936.             );
  1937.         } else {
  1938.             $mandatoryParams = ['version''request''layers''crs''bbox''format''width''height''tiled'];
  1939.             foreach ($mandatoryParams as $param) {
  1940.                 if (!isset($params[$param]) || empty($params[$param])) {
  1941.                     $missingParams[] = $param;
  1942.                 }
  1943.             }
  1944.             if (!empty($missingParams)) {
  1945.                 // Throw an exception with a message that includes the missing parameters
  1946.                 $parameterList implode(", "$missingParams);
  1947.                 throw new \InvalidArgumentException(sprintf($this->translator->trans("missing_or_empty_mandatory_parameter: %s"), $parameterList));
  1948.             }
  1949.             header('Content-Type: image/png');
  1950.             $result $this->meteomaticsWeatherService->getWeatherMap($params['version'], $params['request'], $params['layers'], $params['crs'], $params['bbox'], $params['format'], $params['width'], $params['height'], $params['tiled']);
  1951.             echo $result;
  1952.             exit;
  1953.         }
  1954.         // You can return or process the $data as needed
  1955.         // For demonstration purposes, we will just return a JSON response
  1956.         return $this->json($response);
  1957.     }
  1958.     /**
  1959.      * @Route("/api/public/weather/top-ten-weather-station", methods={"POST"})
  1960.      */
  1961.     public function topTenWeatherStation(Request $request): JsonResponse
  1962.     {
  1963.         try {
  1964.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1965.             if ($response['success'] !== true) {
  1966.                 return $this->json($response);
  1967.             }
  1968.             $params  json_decode($request->getContent(), true);
  1969.             // $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1970.             $requiredParameters = ['parameter''from_date''to_date'];
  1971.             foreach ($requiredParameters as $param) {
  1972.                 if (!isset($params[$param])) {
  1973.                     $missingParams[] = $param;
  1974.                 }
  1975.             }
  1976.             if (!empty($missingParams)) {
  1977.                 // Throw an exception with a message that includes the missing parameters
  1978.                 $parameterList implode(", "$missingParams);
  1979.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  1980.             }
  1981.             $lang = (isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  1982.             $result $this->weatherStationModel->getTopTenWeatherStations($params['parameter'], $params['from_date'], $params['to_date'], $lang);
  1983.             return $this->json($result);
  1984.         } catch (\Exception $ex) {
  1985.             $this->logger->error($ex->getMessage());
  1986.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  1987.         }
  1988.     }
  1989.     /**
  1990.      * @Route("/api/public/weather/get-historical-data-parameters", methods={"POST"})
  1991.      */
  1992.     public function getHistoricalDataParameters(Request $request): JsonResponse
  1993.     {
  1994.         try {
  1995.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  1996.             if ($response['success'] !== true) {
  1997.                 return $this->json($response);
  1998.             }
  1999.             $data = [];
  2000.             $historicalDataParameters = new HistoricalDataParameters\Listing();
  2001.             foreach ($historicalDataParameters as $key => $historicalDataParameter) {
  2002.                 if ($historicalDataParameter) {
  2003.                     $data[] = [
  2004.                         "id" => $historicalDataParameter->getId(),
  2005.                         "key" => $historicalDataParameter->getParameterKey(),
  2006.                         "unit" => $historicalDataParameter->getUnit('en'),
  2007.                         "unit_ar" => $historicalDataParameter->getUnit('ar'),
  2008.                         "name" => $historicalDataParameter->getName('en'),
  2009.                         "name_ar" => $historicalDataParameter->getName('ar')
  2010.                     ];
  2011.                 }
  2012.             }
  2013.             return $this->json(['success' => true'data' => $data]);
  2014.         } catch (\Exception $ex) {
  2015.             $this->logger->error($ex->getMessage());
  2016.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2017.         }
  2018.     }
  2019.     /**
  2020.      * @Route("api/public/weather-data/{start_date}/{end_date}/{resolution}/{parameter}/{coordinate}/{format}", name="api_dynamic_temperature_weather_data", methods={"GET"})
  2021.      */
  2022.     public function getDynamicTemperatureWeatherData(
  2023.         Request $request,
  2024.         string $start_date,
  2025.         string $end_date,
  2026.         string $resolution,
  2027.         string $parameter,
  2028.         string $coordinate,
  2029.         string $format
  2030.     ): Response {
  2031.         try {
  2032.             // Check user authorization
  2033.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2034.             if ($response['success'] !== true) {
  2035.                 return $this->json($response);
  2036.             }
  2037.             $user $response['user'];
  2038.             // Optional query parameters
  2039.             $queryParams $request->query->all();
  2040.             // Build Meteomatics-style path
  2041.             // Example: 2025-12-06T00:00:00Z--2025-12-07T00:00:00Z:PT1H/t_2m:C,t_20m:C/.../json
  2042.             $path sprintf(
  2043.                 "%s--%s:%s/%s/%s",
  2044.                 $start_date,
  2045.                 $end_date,
  2046.                 $resolution,
  2047.                 $parameter,
  2048.                 $coordinate
  2049.             );
  2050.             // Add format at the end
  2051.             $path .= '/' $format;
  2052.             // Call Meteomatics API service
  2053.             $weatherData $this->meteomaticApiService->getDynamicWeatherData($path$queryParams$user);
  2054.             return $weatherData;
  2055.         } catch (\Exception $ex) {
  2056.             $this->logger->error($ex->getMessage());
  2057.             return $this->json([
  2058.                 'success' => false,
  2059.                 'message' => $this->translator->trans(USER_ERROR_MESSAGE)
  2060.             ]);
  2061.         }
  2062.     }
  2063.     /**
  2064.      * @Route("/api/public/get-alert-type", methods={"POST"})
  2065.      */
  2066.     public function getAlertTypeAction(Request $request): JsonResponse
  2067.     {
  2068.         try {
  2069.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2070.             if ($response['success'] !== true) {
  2071.                 return $this->json($response);
  2072.             }
  2073.             $result $this->ewsNotificationModel->getAlertTypes();
  2074.             return $this->json($result);
  2075.         } catch (\Exception $ex) {
  2076.             $this->logger->error($ex->getMessage());
  2077.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  2078.         }
  2079.     }
  2080.     /**
  2081.      * @Route("/api/public/get-alert-action", methods={"POST"})
  2082.      */
  2083.     public function getAlertActionAction(Request $request): JsonResponse
  2084.     {
  2085.         try {
  2086.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2087.             if ($response['success'] !== true) {
  2088.                 return $this->json($response);
  2089.             }
  2090.             $params json_decode($request->getContent(), true);
  2091.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2092.             $result $this->ewsNotificationModel->getAlertActions();
  2093.             return $this->json($result);
  2094.         } catch (\Exception $ex) {
  2095.             $this->logger->error($ex->getMessage());
  2096.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  2097.         }
  2098.     }
  2099.     /**
  2100.      * @Route("/api/public/get-phenomena-list-by-alerts", methods={"POST"})
  2101.      */
  2102.     public function getPhenomenaListByAlertsAction(Request $request): JsonResponse
  2103.     {
  2104.         try {
  2105.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2106.             if ($response['success'] !== true) {
  2107.                 return $this->json($response);
  2108.             }
  2109.             $params json_decode($request->getContent(), true);
  2110.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2111.             if (!isset($params['alert_id']) && !empty($params['alert_id'])) {
  2112.                 throw new \Exception('Missing required alert id');
  2113.             }
  2114.             $alertType $params['alert_id'];
  2115.             // $result = $this->ewsNotificationModel->getPhenomenaListByAlertIds($alertType);
  2116.             $result $this->ewsNotificationModel->getAlertStatuses($params);
  2117.             return $this->json($result);
  2118.         } catch (\Exception $ex) {
  2119.             $this->logger->error($ex->getMessage());
  2120.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  2121.         }
  2122.     }
  2123.     /**
  2124.      * @Route("/api/public/get-regions-list", methods={"POST"})
  2125.      */
  2126.     public function getRegionsListAction(Request $request): JsonResponse
  2127.     {
  2128.         try {
  2129.             $params json_decode($request->getContent(), true);
  2130.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2131.             // check user credentials and expiry
  2132.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2133.             if ($response['success'] !== true) {
  2134.                 return $this->json($response);
  2135.             }
  2136.             $search = (isset($params['search']) && !empty($params['search'])) ? $params['search'] : null;
  2137.             $id = (isset($params['id']) && !empty($params['id'])) ? $params['id'] : null;
  2138.             $result $this->ncmWeatherApiService->getRegionsByName($search$id, isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2139.             return $this->json($result);
  2140.         } catch (\Exception $ex) {
  2141.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  2142.         }
  2143.     }
  2144.     /**
  2145.      * @Route("/api/public/get-governorates-list", methods={"POST"})
  2146.      */
  2147.     public function getGovernoratesListAction(Request $request): JsonResponse
  2148.     {
  2149.         try {
  2150.             $params json_decode($request->getContent(), true);
  2151.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2152.             // check user credentials and expiry
  2153.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2154.             if ($response['success'] !== true) {
  2155.                 return $this->json($response);
  2156.             }
  2157.             $result $this->ewsNotificationModel->getGovernoratesByParams($params);
  2158.             return $this->json($result);
  2159.         } catch (\Exception $ex) {
  2160.             $this->logger->error($ex->getMessage());
  2161.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  2162.         }
  2163.     }
  2164.     /**
  2165.      * @Route("/api/public/get-municipality-list", methods={"POST"})
  2166.      */
  2167.     public function getMunicipalityListAction(Request $request): JsonResponse
  2168.     {
  2169.         try {
  2170.             $params json_decode($request->getContent(), true);
  2171.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2172.             // check user credentials and expiry
  2173.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2174.             if ($response['success'] !== true) {
  2175.                 return $this->json($response);
  2176.             }
  2177.             if (!isset($params['governorate_id'])) {
  2178.                 throw new \Exception('Missing required governorate id');
  2179.             }
  2180.             $governorateId $params['governorate_id'];
  2181.             $search = (isset($params['search']) && !empty($params['search'])) ? $params['search'] : null;
  2182.             $result $this->ewsNotificationModel->getMunicipalityByParams($governorateId$search$params['lang']);
  2183.             return $this->json($result);
  2184.         } catch (\Exception $ex) {
  2185.             $this->logger->error($ex->getMessage());
  2186.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  2187.         }
  2188.     }
  2189.     /**
  2190.      * @Route("/api/public/list-tbase-crops", methods={"POST"})
  2191.      */
  2192.      public function listTbaseCropsAction(Request $requestPaginatorInterface $paginator)
  2193.      {
  2194.          try {
  2195.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2196.             if ($response['success'] !== true) {
  2197.                 return $this->json($response);
  2198.             }
  2199.             $params json_decode($request->getContent(), true);
  2200.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2201.              $crops $this->agricultureModel->getTbaseCrops($params$this->translator$paginator);
  2202.              return $this->json(['success' => true'data' => $crops]);
  2203.          } catch (\Exception $ex) {
  2204.              $this->logger->error($ex->getMessage());
  2205.              return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2206.          }
  2207.     }
  2208.     /**
  2209.      * @Route("/api/public/get-chill-units", methods={"POST"})
  2210.     */
  2211.     public function getChillUnitsAction(Request $request): JsonResponse
  2212.     {
  2213.         try {
  2214.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2215.             if ($response['success'] !== true) {
  2216.                 return $this->json($response);
  2217.             }
  2218.             $params json_decode($request->getContent(), true);
  2219.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2220.             $isAccumulated $params['isAccumulated'] ?? false;
  2221.             $latitude $params['latitude'] ?? null;
  2222.             $longitude $params['longitude'] ?? null;
  2223.             $coordinate "$latitude,$longitude";
  2224.             $parametersArray = ['t_min_2m_1h:C''t_max_2m_1h:C'];
  2225.             $startDate = new DateTime($params['startDate']);
  2226.             $endDate = new DateTime($params['endDate']);
  2227.             
  2228.             $resolution 'PT1H';
  2229.             $format 'json';
  2230.             $model 'mix';
  2231.             $source '';
  2232.             $onInvalid '';
  2233.             
  2234.             $response $this->meteomaticApiService->publicRouteQuery(
  2235.                 $startDate,
  2236.                 $endDate,
  2237.                 $resolution,
  2238.                 $parametersArray,
  2239.                 $coordinate,
  2240.                 $format,
  2241.                 $model,
  2242.                 $source,
  2243.                 $onInvalid,
  2244.             );
  2245.             $hourlyData $this->chillUnitService->transformMeteomaticsResponse($response['data']);
  2246.             if ($isAccumulated) {
  2247.                 $result $this->chillUnitService->calculateAccumulated($hourlyData);
  2248.             } else {
  2249.                 $result $this->chillUnitService->calculate($hourlyData);
  2250.             }
  2251.             return $this->json($result);
  2252.         } catch (\Exception $ex) {
  2253.             $this->logger->error($ex->getMessage());
  2254.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2255.         }
  2256.     }
  2257.     /**
  2258.      * @Route("/api/public/get-water-requirements", methods={"POST"})
  2259.     */
  2260.     public function getWaterRequirementAction(Request $request): JsonResponse
  2261.     {
  2262.         try {
  2263.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2264.             if ($response['success'] !== true) {
  2265.                 return $this->json($response);
  2266.             }
  2267.             $params json_decode($request->getContent(), true);
  2268.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2269.             $regionIds = [];
  2270.             
  2271.             $latitude $params['latitude'] ?? null;
  2272.             $longitude $params['longitude'] ?? null;
  2273.             $coordinate "$latitude,$longitude";
  2274.             
  2275.             $governorateArr \App\Lib\Utility::processLocationCoordinates([[$latitude$longitude]]);
  2276.             foreach ($governorateArr as $governorateName) {
  2277.                 if ($governorateName['en']) {
  2278.                     $governorate DataObject\Governorate::getByName($governorateName['en'],'en',true);
  2279.                     if ($governorate instanceof DataObject\Governorate) {
  2280.                         // Assuming getRegion() returns a Region DataObject
  2281.                         $region $governorate->getRegionId();
  2282.                         $regionIds[] = $region->getId();
  2283.                     }
  2284.                 }
  2285.             }
  2286.             $parametersArray = ['evapotranspiration_24h:mm'];
  2287.             $startDate = new DateTime();
  2288.             $endDate = new DateTime();
  2289.             $resolution 'PT24H';
  2290.             $format 'json';
  2291.             $model 'mix';
  2292.             $source '';
  2293.             $onInvalid '';
  2294.             $kcKeys $params['kcKeys'] ?? ['kcInitial''kcMid''kcEnd'];
  2295.             $cropNames $params['cropNames'] ?? [];
  2296.             $response $this->meteomaticApiService->publicRouteQuery(
  2297.                 $startDate,
  2298.                 $endDate,
  2299.                 $resolution,
  2300.                 $parametersArray,
  2301.                 $coordinate,
  2302.                 $format,
  2303.                 $model,
  2304.                 $source,
  2305.                 $onInvalid,
  2306.             );
  2307.             $meteoData $response['data']['data'][0]['coordinates'];
  2308.             $cropsData $this->agricultureModel->getCropsWithRegion($regionIds$cropNames$kcKeys$meteoData);
  2309.             return $this->json($cropsData);
  2310.          } catch (\Exception $ex) {
  2311.             $this->logger->error($ex->getMessage());
  2312.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2313.         }
  2314.     }
  2315.     /**
  2316.      * @Route("/api/public/calculate-gdd-and-gts", methods={"POST"})
  2317.      */
  2318.     public function calculateGDDandGTSAction(Request $request): JsonResponse
  2319.     {
  2320.         try {
  2321.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2322.             if ($response['success'] !== true) {
  2323.                 return $this->json($response);
  2324.             }
  2325.             $params json_decode($request->getContent(), true);
  2326.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2327.             $params json_decode($request->getContent(), true);
  2328.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2329.             $requiredParameters = [
  2330.                 'coordinates',
  2331.                 'cropIds',
  2332.                 'startDate',
  2333.                 'endDate'
  2334.             ];
  2335.          
  2336.             foreach ($requiredParameters as $param) {
  2337.                 if (!isset($params[$param])) {
  2338.                     $missingParams[] = $param;
  2339.                 }
  2340.             }
  2341.             if (!empty($missingParams)) {
  2342.                 // Throw an exception with a message that includes the missing parameters
  2343.                 $parameterList implode(", "$missingParams);
  2344.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  2345.             }
  2346.             $params['meteomaticApiService'] = $this->meteomaticApiService;
  2347.             $gddAndGtsData $this->agricultureModel->calculateGDDandGTS($params,$this->translator);
  2348.             return $this->json($gddAndGtsData);
  2349.         } catch (\Exception $ex) {
  2350.             $this->logger->error($ex->getMessage());
  2351.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2352.         }
  2353.     }
  2354.     /**
  2355.      * @Route("/api/public/crops", methods={"POST"})
  2356.      */
  2357.     public function cropsAction(Request $request): JsonResponse
  2358.     {
  2359.         try {
  2360.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2361.             if ($response['success'] !== true) {
  2362.                 return $this->json($response);
  2363.             }
  2364.             $params json_decode($request->getContent(), true);
  2365.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2366.             $result $this->agricultureModel->getCrops($request$params$this->translator);
  2367.             return $this->json($result);
  2368.         } catch (\Exception $ex) {
  2369.             $this->logger->error($ex->getMessage());
  2370.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2371.         }
  2372.     }
  2373.     /**
  2374.      * @Route("/api/public/list-location", methods={"POST"})
  2375.      */
  2376.     public function listLocation(Request $requestPaginatorInterface $paginator): JsonResponse
  2377.     {
  2378.         try {
  2379.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2380.             if ($response['success'] !== true) {
  2381.                 return $this->json($response);
  2382.             }
  2383.             $applicationName $request->headers->get('X-App-Name');
  2384.             $applicationConsumer $request->headers->get('X-App-Consumer-Key');
  2385.             if (!$applicationName || !$applicationConsumer) {
  2386.                 return $this->json(['success' => false,'message' => 'Missing required headers']);
  2387.             }
  2388.             $user DataObject\Customer::getByEmail($applicationNametrue);
  2389.             if (!$user) {
  2390.                 return $this->json([
  2391.                     'success' => false,
  2392.                     'message' => 'Invalid User'
  2393.                 ]);
  2394.             }
  2395.             $application DataObject\WSO2Applications::getByUser($usertrue);
  2396.             if (!$application) {
  2397.                 return $this->json([
  2398.                     'success' => false,
  2399.                     'message' => 'User not found'
  2400.                 ]);
  2401.             }
  2402.             // 5. Consumer key validation
  2403.             if ($application->getConsumerKey() !== $applicationConsumer) {
  2404.                 return $this->json([
  2405.                     'success' => false,
  2406.                     'message' => 'Invalid Consumer Key'
  2407.                 ]);
  2408.             }
  2409.             $params json_decode($request->getContent(), true);
  2410.             $this->translator->setlocale($params['lang'] ?? DEFAULT_LOCALE);
  2411.             $result $this->locationModel->locationListing(
  2412.                 $request,
  2413.                 $user,
  2414.                 $params,
  2415.                 $paginator,
  2416.                 $this->translator
  2417.             );
  2418.             return $this->json($result);
  2419.         } catch (\Exception $ex) {
  2420.             $this->logger->error($ex->getMessage());
  2421.             return $this->json([
  2422.                 'success' => false,
  2423.                 'message' => $ex->getMessage()
  2424.             ]);
  2425.         }
  2426.     }
  2427.     /**
  2428.      * @Route("/api/public/list-risk-category", methods={"POST"})
  2429.      */
  2430.     public function getRiskCategories(Request $requestPaginatorInterface $paginator)
  2431.     {
  2432.        try {
  2433.            $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2434.            if ($response['success'] !== true) {
  2435.                return $this->json($response);
  2436.            }
  2437.            $applicationName $request->headers->get('X-App-Name');
  2438.            $applicationConsumer $request->headers->get('X-App-Consumer-Key');
  2439.            if (!$applicationName || !$applicationConsumer) {
  2440.                return $this->json(['success' => false,'message' => 'Missing required headers']);
  2441.            }
  2442.            $user DataObject\Customer::getByEmail($applicationNametrue);
  2443.            if (!$user) {
  2444.                return $this->json([
  2445.                    'success' => false,
  2446.                    'message' => 'Invalid User'
  2447.                ]);
  2448.            }
  2449.            $application DataObject\WSO2Applications::getByUser($usertrue);
  2450.            if (!$application) {
  2451.                return $this->json([
  2452.                    'success' => false,
  2453.                    'message' => 'User not found'
  2454.                ]);
  2455.            }
  2456.            // 5. Consumer key validation
  2457.            if ($application->getConsumerKey() !== $applicationConsumer) {
  2458.                return $this->json([
  2459.                    'success' => false,
  2460.                    'message' => 'Invalid Consumer Key'
  2461.                ]);
  2462.            }
  2463.            $params json_decode($request->getContent(), true);
  2464.            $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2465.     
  2466.            $requiredParameters = [];
  2467.            foreach ($requiredParameters as $param) {
  2468.                if (!isset($params[$param])) {
  2469.                    $missingParams[] = $param;
  2470.                }
  2471.            }
  2472.            if (!empty($missingParams)) {
  2473.                // Throw an exception with a message that includes the missing parameters
  2474.                $parameterList implode(", "$missingParams);
  2475.                return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  2476.            }
  2477.            
  2478.            $result $this->weatherParameterModel->riskCategoryFetch($params$paginator$user);
  2479.            return $this->json($result); 
  2480.        } catch (\Exception $ex) {
  2481.            $this->logger->error($ex->getMessage());
  2482.            return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2483.        }
  2484.     }
  2485.     /**
  2486.      * @Route("/api/public/locations-for-map", methods={"POST"})
  2487.      */
  2488.     public function getLocationsForMaps(Request $request): JsonResponse
  2489.     {
  2490.        try {
  2491.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2492.             if ($response['success'] !== true) {
  2493.                 return $this->json($response);
  2494.             }
  2495.             $applicationName $request->headers->get('X-App-Name');
  2496.             $applicationConsumer $request->headers->get('X-App-Consumer-Key');
  2497.  
  2498.             if (!$applicationName || !$applicationConsumer) {
  2499.                 return $this->json(['success' => false,'message' => 'Missing required headers']);
  2500.             }
  2501.  
  2502.             $user DataObject\Customer::getByEmail($applicationNametrue);
  2503.             if (!$user) {
  2504.                 return $this->json([
  2505.                     'success' => false,
  2506.                     'message' => 'Invalid User'
  2507.                 ]);
  2508.             }
  2509.  
  2510.             $application DataObject\WSO2Applications::getByUser($usertrue);
  2511.             if (!$application) {
  2512.                 return $this->json([
  2513.                     'success' => false,
  2514.                     'message' => 'User not found'
  2515.                 ]);
  2516.             }
  2517.  
  2518.             // 5. Consumer key validation
  2519.             if ($application->getConsumerKey() !== $applicationConsumer) {
  2520.                 return $this->json([
  2521.                     'success' => false,
  2522.                     'message' => 'Invalid Consumer Key'
  2523.                 ]);
  2524.             }
  2525.             $params json_decode($request->getContent(), true);
  2526.             $parameter $params['parameter'] ?? null;
  2527.             $duration $params['duration'] ?? null;
  2528.             $dateTime $params['datetime'] ?? null;
  2529.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2530.             if (!$parameter || !$duration || !$dateTime) {
  2531.                 return new JsonResponse(['success' => false'message' => $this->translator->trans('missing_parameter_or_duration_or_date')], 400);
  2532.             }
  2533.             $results $this->weatherParameterModel->getLocationsForMaps($params$parameter$duration$dateTime$user$this->translator);
  2534.            return $this->json(['success' => true'data' => $results]); 
  2535.        } catch (\Exception $ex) {
  2536.            $this->logger->error($ex->getMessage());
  2537.            return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2538.        }
  2539.     }
  2540.     /**
  2541.      * @Route("/api/public/locations-by-dates", methods={"POST"})
  2542.      */
  2543.     public function getLocationsByDates(Request $request): JsonResponse
  2544.     {
  2545.        try {
  2546.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2547.             if ($response['success'] !== true) {
  2548.                 return $this->json($response);
  2549.             }
  2550.             $applicationName $request->headers->get('X-App-Name');
  2551.             $applicationConsumer $request->headers->get('X-App-Consumer-Key');
  2552.  
  2553.             if (!$applicationName || !$applicationConsumer) {
  2554.                 return $this->json(['success' => false,'message' => 'Missing required headers']);
  2555.             }
  2556.  
  2557.             $user DataObject\Customer::getByEmail($applicationNametrue);
  2558.             if (!$user) {
  2559.                 return $this->json([
  2560.                     'success' => false,
  2561.                     'message' => 'Invalid User'
  2562.                 ]);
  2563.             }
  2564.  
  2565.             $application DataObject\WSO2Applications::getByUser($usertrue);
  2566.             if (!$application) {
  2567.                 return $this->json([
  2568.                     'success' => false,
  2569.                     'message' => 'User not found'
  2570.                 ]);
  2571.             }
  2572.  
  2573.             // 5. Consumer key validation
  2574.             if ($application->getConsumerKey() !== $applicationConsumer) {
  2575.                 return $this->json([
  2576.                     'success' => false,
  2577.                     'message' => 'Invalid Consumer Key'
  2578.                 ]);
  2579.             }
  2580.             $params json_decode($request->getContent(), true);
  2581.             $parameter $params['parameter'] ?? null;
  2582.             $duration $params['duration'] ?? null;
  2583.             $dateTime $params['datetime'] ?? null;
  2584.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2585.             if (!$parameter || !$duration || !$dateTime) {
  2586.                 return new JsonResponse(['success' => false'message' => $this->translator->trans('missing_parameter_or_duration_or_date')], 400);
  2587.             }
  2588.            $results $this->weatherParameterModel->getLocationsbyDates($parameter$duration$dateTime$this->translator);
  2589.            return $this->json(['success' => true"lastUpdated" => $results['last_updated_at'], 'data' => $results['data']]); 
  2590.        } catch (\Exception $ex) {
  2591.            $this->logger->error($ex->getMessage());
  2592.            return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2593.        }
  2594.     }
  2595.     /**
  2596.      * @Route("/api/public/locations-by-tags", methods={"POST"})
  2597.      */
  2598.     public function getLocationsByTags(Request $request): JsonResponse
  2599.     {
  2600.        try {
  2601.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2602.             if ($response['success'] !== true) {
  2603.                 return $this->json($response);
  2604.             }
  2605.             $applicationName $request->headers->get('X-App-Name');
  2606.             $applicationConsumer $request->headers->get('X-App-Consumer-Key');
  2607.             if (!$applicationName || !$applicationConsumer) {
  2608.                 return $this->json(['success' => false,'message' => 'Missing required headers']);
  2609.             }
  2610.             $user DataObject\Customer::getByEmail($applicationNametrue);
  2611.             if (!$user) {
  2612.                 return $this->json([
  2613.                     'success' => false,
  2614.                     'message' => 'Invalid User'
  2615.                 ]);
  2616.             }
  2617.             $application DataObject\WSO2Applications::getByUser($usertrue);
  2618.             if (!$application) {
  2619.                 return $this->json([
  2620.                     'success' => false,
  2621.                     'message' => 'User not found'
  2622.                 ]);
  2623.             }
  2624.             // 5. Consumer key validation
  2625.             if ($application->getConsumerKey() !== $applicationConsumer) {
  2626.                 return $this->json([
  2627.                     'success' => false,
  2628.                     'message' => 'Invalid Consumer Key'
  2629.                 ]);
  2630.             }
  2631.             $params json_decode($request->getContent(), true);
  2632.             $parameter $params['parameter'] ?? null;
  2633.             $duration $params['duration'] ?? null;
  2634.             $dateTime $params['datetime'] ?? null;
  2635.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2636.             if (!$parameter || !$duration || !$dateTime) {
  2637.                 return $this->json(['success' => false'message' => $this->translator->trans('missing_parameter_or_duration_or_date')], 400);
  2638.             }
  2639.             $tags = [];
  2640.             $tagListing = new WeatherParameterLocationTags\Listing();
  2641.             $tagListing->setCondition("`default` = ?", [true]);
  2642.             foreach ($tagListing as $tag) {
  2643.                 $tags[] = [
  2644.                     'id' => $tag->getId(),
  2645.                     'nameEn' => $tag->getNameEn(),
  2646.                     'nameAr' => $tag->getNameAr(),
  2647.                 ];
  2648.             }
  2649.             $desiredOrder = ['High risk''Medium risk''Low risk'];
  2650.             usort($tags, function ($a$b) use ($desiredOrder) {
  2651.                 $posA array_search($a['nameEn'], $desiredOrder);
  2652.                 $posB array_search($b['nameEn'], $desiredOrder);
  2653.                 return $posA <=> $posB;
  2654.             });
  2655.             $results $this->weatherParameterModel->getLocationsByTags($parameter$duration$dateTime$tags$this->translator);
  2656.            return $this->json(['success' => true'data' => $results]); 
  2657.        } catch (\Exception $ex) {
  2658.            $this->logger->error($ex->getMessage());
  2659.            return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2660.        }
  2661.     }
  2662.     /**
  2663.      * @Route("/api/public/locations-by-risk", methods={"POST"})
  2664.      */
  2665.     public function getLocationsByRisk(Request $request): JsonResponse
  2666.     {
  2667.        try {
  2668.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2669.             if ($response['success'] !== true) {
  2670.                 return $this->json($response);
  2671.             }
  2672.             $applicationName $request->headers->get('X-App-Name');
  2673.             $applicationConsumer $request->headers->get('X-App-Consumer-Key');
  2674.             if (!$applicationName || !$applicationConsumer) {
  2675.                 return $this->json(['success' => false,'message' => 'Missing required headers']);
  2676.             }
  2677.             $user DataObject\Customer::getByEmail($applicationNametrue);
  2678.             if (!$user) {
  2679.                 return $this->json([
  2680.                     'success' => false,
  2681.                     'message' => 'Invalid User'
  2682.                 ]);
  2683.             }
  2684.             $application DataObject\WSO2Applications::getByUser($usertrue);
  2685.             if (!$application) {
  2686.                 return $this->json([
  2687.                     'success' => false,
  2688.                     'message' => 'User not found'
  2689.                 ]);
  2690.             }
  2691.             // 5. Consumer key validation
  2692.             if ($application->getConsumerKey() !== $applicationConsumer) {
  2693.                 return $this->json([
  2694.                     'success' => false,
  2695.                     'message' => 'Invalid Consumer Key'
  2696.                 ]);
  2697.             }
  2698.             $params json_decode($request->getContent(), true);
  2699.             $parameter $params['parameter'] ?? null;
  2700.             $duration $params['duration'] ?? null;
  2701.             $dateTime $params['datetime'] ?? null;
  2702.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2703.             if (!$parameter || !$duration || !$dateTime) {
  2704.                 return $this->json(['success' => false'message' => $this->translator->trans('missing_parameter_or_duration_or_date')], 400);
  2705.             }
  2706.             $results $this->weatherParameterModel->getLocationsByRiskCat($params$parameter$duration$dateTime$user$this->translator);
  2707.            return $this->json(['success' => true'data' => $results]); 
  2708.        } catch (\Exception $ex) {
  2709.            $this->logger->error($ex->getMessage());
  2710.            return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2711.        }
  2712.     }
  2713.     /**
  2714.      * @Route("/api/public/locations-by-amana", methods={"POST"})
  2715.      */
  2716.     public function getLocationsByAmana(Request $request): JsonResponse
  2717.     {
  2718.        try {
  2719.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2720.             if ($response['success'] !== true) {
  2721.                 return $this->json($response);
  2722.             }
  2723.             $applicationName $request->headers->get('X-App-Name');
  2724.             $applicationConsumer $request->headers->get('X-App-Consumer-Key');
  2725.             if (!$applicationName || !$applicationConsumer) {
  2726.                 return $this->json(['success' => false,'message' => 'Missing required headers']);
  2727.             }
  2728.             $user DataObject\Customer::getByEmail($applicationNametrue);
  2729.             if (!$user) {
  2730.                 return $this->json([
  2731.                     'success' => false,
  2732.                     'message' => 'Invalid User'
  2733.                 ]);
  2734.             }
  2735.             $application DataObject\WSO2Applications::getByUser($usertrue);
  2736.             if (!$application) {
  2737.                 return $this->json([
  2738.                     'success' => false,
  2739.                     'message' => 'User not found'
  2740.                 ]);
  2741.             }
  2742.             // 5. Consumer key validation
  2743.             if ($application->getConsumerKey() !== $applicationConsumer) {
  2744.                 return $this->json([
  2745.                     'success' => false,
  2746.                     'message' => 'Invalid Consumer Key'
  2747.                 ]);
  2748.             }
  2749.             $params json_decode($request->getContent(), true);
  2750.             $parameter $params['parameter'] ?? null;
  2751.             $duration $params['duration'] ?? null;
  2752.             $dateTime $params['datetime'] ?? null;
  2753.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2754.             if (!$parameter || !$duration || !$dateTime) {
  2755.                 return new JsonResponse(['success' => false'message' => $this->translator->trans('missing_parameter_or_duration_or_date')], 400);
  2756.             }
  2757.             $results $this->weatherParameterModel->getLocationsByAmanaId($params$parameter$duration$dateTime$this->translator);
  2758.            return $this->json(['success' => true'data' => $results]); 
  2759.        } catch (\Exception $ex) {
  2760.            $this->logger->error($ex->getMessage());
  2761.            return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2762.        }
  2763.     }
  2764.     /**
  2765.      * @Route("/api/public/weather-reports/list", name="ncm_weather_reports_list", methods={"GET"})
  2766.      */
  2767.     public function listReports(Request $request): JsonResponse
  2768.     {
  2769.         try {
  2770.             
  2771.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2772.             if ($response['success'] !== true) {
  2773.                 return $this->json($response);
  2774.             }
  2775.             $user $response['user'];
  2776.             $params json_decode($request->getContent(), true);
  2777.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2778.             
  2779.             
  2780.             $folder Asset::getByPath("/weather-reports");
  2781.             if (!$folder) {
  2782.                 return new JsonResponse([
  2783.                     'status' => 'error',
  2784.                     'message' => 'No weather reports found.'
  2785.                 ], 404);
  2786.             }
  2787.             $reports = [];
  2788.             foreach ($folder->getChildren() as $asset) {
  2789.                 if ($asset instanceof Asset) {
  2790.                     $title $asset->getMetadata('title''ar');
  2791.                     $reports[] = [
  2792.                         'name' => $asset->getFilename(),
  2793.                         'link' => $asset->getFullPath(),
  2794.                         'title' => ($title !== null && $title !== '') ? $title null,
  2795.                     ];
  2796.                 }
  2797.             }
  2798.             return new JsonResponse([
  2799.                 'status' => 'success',
  2800.                 'reports' => $reports
  2801.             ]);
  2802.         } catch (\Throwable $e) {
  2803.             return new JsonResponse([
  2804.                 'status' => 'error',
  2805.                 'message' => $e->getMessage()
  2806.             ], 500);
  2807.         }
  2808.     }
  2809.     /** 
  2810.      * @Route("/api/public/create-location", methods={"POST"})
  2811.      */
  2812.     public function createLocationPublic(Request $request): JsonResponse
  2813.     {
  2814.         try {
  2815.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  2816.             if ($response['success'] !== true) {
  2817.                 return $this->json($response);
  2818.             }
  2819.             $applicationName $request->headers->get('X-App-Name');
  2820.             $applicationConsumer $request->headers->get('X-App-Consumer-Key');
  2821.             if (!$applicationName || !$applicationConsumer) {
  2822.                 return $this->json(['success' => false,'message' => 'Missing required headers']);
  2823.             }
  2824.             $user DataObject\Customer::getByEmail($applicationNametrue);
  2825.             if (!$user) {
  2826.                 return $this->json([
  2827.                     'success' => false,
  2828.                     'message' => 'Invalid User'
  2829.                 ]);
  2830.             }
  2831.             $application DataObject\WSO2Applications::getByUser($usertrue);
  2832.             if (!$application) {
  2833.                 return $this->json([
  2834.                     'success' => false,
  2835.                     'message' => 'User not found'
  2836.                 ]);
  2837.             }
  2838.             // 5. Consumer key validation
  2839.             if ($application->getConsumerKey() !== $applicationConsumer) {
  2840.                 return $this->json([
  2841.                     'success' => false,
  2842.                     'message' => 'Invalid Consumer Key'
  2843.                 ]);
  2844.             }
  2845.             
  2846.             $params  json_decode($request->getContent(), true);
  2847.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  2848.             $requiredParameters = ['coordinates'];
  2849.             foreach ($requiredParameters as $param) {
  2850.                 if (!isset($params[$param]) || empty($params[$param])) {
  2851.                     $missingParams[] = $param;
  2852.                 }
  2853.             }
  2854.             if (!empty($missingParams)) {
  2855.                 $parameterList implode(", "$missingParams);
  2856.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  2857.             }
  2858.             //location address components
  2859.       
  2860.             // Validate and geocode coordinates
  2861.             try {
  2862.                 $geocodingUrl sprintf(
  2863.                     'https://maps.googleapis.com/maps/api/geocode/json?key=%s&latlng=%s,%s&language=%s',
  2864.                     $_ENV['GOOGLE_API_KEY'],
  2865.                     $params['coordinates'][0][0],
  2866.                     $params['coordinates'][0][1],
  2867.                     $params['lang'] ?? 'en'
  2868.                 );
  2869.                 $response $this->httpClient->get($geocodingUrl);
  2870.                 $response json_decode($response->getBody()->getContents(), true);
  2871.                 
  2872.                 // Check if geocoding was successful
  2873.                 if (!isset($response['status']) || $response['status'] !== 'OK' || empty($response['results'])) {
  2874.                     return $this->json([
  2875.                         'success' => false,
  2876.                         'message' => $this->translator->trans("Invalid coordinates")
  2877.                     ]);
  2878.                 }
  2879.                 
  2880.                 $params['address_components'] = $response['results'][0]['address_components'] ?? [];
  2881.                 $params['name'] = $this->extractLocality($response);
  2882.             } catch (\Exception $e) {
  2883.                 return $this->json([
  2884.                     'success' => false,
  2885.                     'message' => $this->translator->trans("Invalid coordinates")
  2886.                 ]);
  2887.             }
  2888.             $result $this->locationModel->createLocation($request$params$user$this->translator);
  2889.             return $this->json($result);
  2890.         } catch (\Exception $ex) {
  2891.             $this->logger->error($ex->getMessage());
  2892.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  2893.         }
  2894.     }
  2895.     private function extractLocality(array $response): string
  2896.     {
  2897.         if ($response['status'] === 'OK') {
  2898.             foreach ($response['results'] as $result) {
  2899.                 foreach ($result['address_components'] as $component) {
  2900.                     if (in_array('locality'$component['types'])) {
  2901.                         return $component['long_name'];
  2902.                     }
  2903.                 }
  2904.             }
  2905.         }
  2906.         return '';
  2907.     }
  2908.     /**
  2909.      * @Route("/api/public/bulk-location-upload", methods={"POST"})
  2910.      */
  2911.     public function bulkLocationUpload(Request $request): JsonResponse
  2912.     {
  2913.         try {
  2914.             // $response = $this->publicUserPermissionService->isAuthorized($request, $this->translator);
  2915.             // if ($response['success'] !== true) {
  2916.             //     return $this->json($response);
  2917.             // }
  2918.             $applicationName $request->headers->get('X-App-Name');
  2919.             $applicationConsumer $request->headers->get('X-App-Consumer-Key');
  2920.             if (!$applicationName || !$applicationConsumer) {
  2921.                 return $this->json(['success' => false'message' => 'Missing required headers']);
  2922.             }
  2923.             $user DataObject\Customer::getByEmail($applicationNametrue);
  2924.             if (!$user) {
  2925.                 return $this->json(['success' => false'message' => 'Invalid User']);
  2926.             }
  2927.             $application DataObject\WSO2Applications::getByUser($usertrue);
  2928.             if (!$application) {
  2929.                 return $this->json(['success' => false'message' => 'User not found']);
  2930.             }
  2931.             if ($application->getConsumerKey() !== $applicationConsumer) {
  2932.                 return $this->json(['success' => false'message' => 'Invalid Consumer Key']);
  2933.             }
  2934.             $lang $request->request->get('lang''en');
  2935.             $this->translator->setlocale($lang);
  2936.             // Get uploaded file
  2937.             $file $request->files->get('file');
  2938.             if (!$file || !$file->isValid()) {
  2939.                 return $this->json(['success' => false'message' => $this->translator->trans('Invalid or missing file')]);
  2940.             }
  2941.             // Validate file extension
  2942.             $allowedExtensions = ['xlsx''xls'];
  2943.             $extension strtolower($file->getClientOriginalExtension());
  2944.             if (!in_array($extension$allowedExtensions)) {
  2945.                 return $this->json(['success' => false'message' => $this->translator->trans('Invalid file format. Please upload Excel file (.xlsx or .xls)')]);
  2946.             }
  2947.             // Parse Excel file
  2948.             $locationData $this->parseExcelFile($file);
  2949.             
  2950.             if (empty($locationData)) {
  2951.                 return $this->json(['success' => false'message' => $this->translator->trans('No valid data found in the Excel file')]);
  2952.             }
  2953.             if (count($locationData) > 2500) {
  2954.                 return $this->json(['success' => false'message' => $this->translator->trans('Maximum 2500 locations allowed per upload')]);
  2955.             }
  2956.             // Get address_components for each location using Google Geocoding API
  2957.             $invalidCoordinates = [];
  2958.             foreach ($locationData as $index => &$location) {
  2959.                 try {
  2960.                     $geocodingUrl sprintf(
  2961.                         'https://maps.googleapis.com/maps/api/geocode/json?key=%s&latlng=%s,%s&language=%s',
  2962.                         $_ENV['GOOGLE_API_KEY'],
  2963.                         $location['lat'],
  2964.                         $location['lng'],
  2965.                         $lang
  2966.                     );
  2967.                     $response $this->httpClient->get($geocodingUrl);
  2968.                     $response json_decode($response->getBody()->getContents(), true);
  2969.                     
  2970.                     if (isset($response['status']) && $response['status'] === 'OK' && !empty($response['results'])) {
  2971.                         $location['address_components'] = $response['results'][0]['address_components'] ?? [];
  2972.                         // If name is not set, try to extract locality
  2973.                         if (empty($location['locationName'])) {
  2974.                             $location['locationName'] = $this->extractLocality($response);
  2975.                         }
  2976.                     } else {
  2977.                         // Mark as invalid coordinates
  2978.                         $invalidCoordinates[] = [
  2979.                             'row' => $index 1,
  2980.                             'locationName' => $location['locationName'] ?? 'Unknown',
  2981.                             'lat' => $location['lat'],
  2982.                             'lng' => $location['lng']
  2983.                         ];
  2984.                         $location['address_components'] = [];
  2985.                     }
  2986.                 } catch (\Exception $e) {
  2987.                     $location['address_components'] = [];
  2988.                 }
  2989.             }
  2990.             unset($location); // Break reference
  2991.             // If there are invalid coordinates, return error with details
  2992.             if (!empty($invalidCoordinates) && count($invalidCoordinates) === count($locationData)) {
  2993.                 return $this->json([
  2994.                     'success' => false,
  2995.                     'message' => $this->translator->trans('All coordinates are invalid'),
  2996.                     'invalid_coordinates' => $invalidCoordinates
  2997.                 ]);
  2998.             }
  2999.             // Prepare params for bulk location creation
  3000.             $params = ['data' => $locationData];
  3001.             $result $this->locationModel->createBulkLocations($request$params$user$this->translator);
  3002.             
  3003.             // Add invalid coordinates info to result if any
  3004.             if (!empty($invalidCoordinates)) {
  3005.                 $result['invalid_coordinates'] = $invalidCoordinates;
  3006.             }
  3007.             
  3008.             return $this->json($result);
  3009.         } catch (\Exception $ex) {
  3010.             $this->logger->error($ex->getMessage());
  3011.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  3012.         }
  3013.     }
  3014.     /**
  3015.      * Parse Excel file and extract location data
  3016.      */
  3017.     private function parseExcelFile($file): array
  3018.     {
  3019.         $locationData = [];
  3020.         
  3021.         try {
  3022.             $spreadsheet \PhpOffice\PhpSpreadsheet\IOFactory::load($file->getPathname());
  3023.             $worksheet $spreadsheet->getActiveSheet();
  3024.             $rows $worksheet->toArray();
  3025.             if (empty($rows)) {
  3026.                 throw new \Exception('Excel file is empty');
  3027.             }
  3028.             // Find the header row (skip instruction rows)
  3029.             $headerRowIndex null;
  3030.             $headers null;
  3031.             $normalizedHeaders null;
  3032.             
  3033.             // Possible header column names to detect
  3034.             $possibleLocationNames = ['locationname''location''name''sitename''site''placename''place''title''label'];
  3035.             $possibleLatNames = ['lat''latitude''lattitude''y'];
  3036.             $possibleLngNames = ['lng''long''longitude''lon''x'];
  3037.             // Search first 10 rows to find the header row
  3038.             $maxSearchRows min(10count($rows));
  3039.             for ($i 0$i $maxSearchRows$i++) {
  3040.                 $row $rows[$i];
  3041.                 
  3042.                 // Normalize this row's values
  3043.                 $normalized array_map(function($h) {
  3044.                     $h strtolower(trim($h ?? ''));
  3045.                     return preg_replace('/[\s_\-]+/'''$h);
  3046.                 }, $row);
  3047.                 
  3048.                 // Check if this row contains header-like columns
  3049.                 $hasLocationCol false;
  3050.                 $hasLatCol false;
  3051.                 $hasLngCol false;
  3052.                 
  3053.                 foreach ($normalized as $col) {
  3054.                     if (in_array($col$possibleLocationNames)) $hasLocationCol true;
  3055.                     if (in_array($col$possibleLatNames)) $hasLatCol true;
  3056.                     if (in_array($col$possibleLngNames)) $hasLngCol true;
  3057.                 }
  3058.                 
  3059.                 // If this row has all required column headers, use it as the header row
  3060.                 if ($hasLocationCol && $hasLatCol && $hasLngCol) {
  3061.                     $headerRowIndex $i;
  3062.                     $headers $row;
  3063.                     $normalizedHeaders $normalized;
  3064.                     break;
  3065.                 }
  3066.             }
  3067.             if ($headerRowIndex === null) {
  3068.                 throw new \Exception('Could not find header row with required columns (Location Name, Latitude, Longitude). Please ensure your Excel has these column headers.');
  3069.             }
  3070.             // Find column indexes with expanded possible names
  3071.             $locationNameIndex $this->findColumnIndex($normalizedHeaders, [
  3072.                 'locationname''location''name''sitename''site''placename''place',
  3073.                 'title''label''اسم''اسمالموقع''الموقع'
  3074.             ]);
  3075.             $latIndex $this->findColumnIndex($normalizedHeaders, [
  3076.                 'lat''latitude''lattitude''y''خطالعرض''عرض'
  3077.             ]);
  3078.             $lngIndex $this->findColumnIndex($normalizedHeaders, [
  3079.                 'lng''long''longitude''lon''x''خطالطول''طول'
  3080.             ]);
  3081.             $tagIndex $this->findColumnIndex($normalizedHeaders, [
  3082.                 'tag''tags''tagname''tagnames''locationtag''locationtags',
  3083.                 'category''categories''group''groups',
  3084.                 'تاج''علامة''فئة'
  3085.             ]);
  3086.             $metaDataIndex $this->findColumnIndex($normalizedHeaders, [
  3087.                 'metadata''meta''description''desc''notes''note''details''info',
  3088.                 'بيانات''وصف''ملاحظات'
  3089.             ]);
  3090.             // Parse data rows (starting after the header row)
  3091.             for ($i $headerRowIndex 1$i count($rows); $i++) {
  3092.                 $row $rows[$i];
  3093.                 
  3094.                 $locationName trim($row[$locationNameIndex] ?? '');
  3095.                 $lat $row[$latIndex] ?? null;
  3096.                 $lng $row[$lngIndex] ?? null;
  3097.                 // Skip empty rows
  3098.                 if (empty($locationName) && ($lat === null || $lat === '') && ($lng === null || $lng === '')) {
  3099.                     continue;
  3100.                 }
  3101.                 // Validate that we have all required data
  3102.                 if (empty($locationName) || $lat === null || $lat === '' || $lng === null || $lng === '') {
  3103.                     continue; // Skip incomplete rows
  3104.                 }
  3105.                 $location = [
  3106.                     'locationName' => $locationName,
  3107.                     'lat' => is_numeric($lat) ? floatval($lat) : $lat,
  3108.                     'lng' => is_numeric($lng) ? floatval($lng) : $lng,
  3109.                 ];
  3110.                 // Add tags if present
  3111.                 if ($tagIndex !== null && !empty($row[$tagIndex])) {
  3112.                     $tagValue trim($row[$tagIndex]);
  3113.                     // Split by comma if multiple tags
  3114.                     $location['tagNames'] = array_map('trim'explode(','$tagValue));
  3115.                 }
  3116.                 // Add metadata if present
  3117.                 if ($metaDataIndex !== null && !empty($row[$metaDataIndex])) {
  3118.                     $location['metaData'] = trim($row[$metaDataIndex]);
  3119.                 }
  3120.                 $locationData[] = $location;
  3121.             }
  3122.         } catch (\Exception $e) {
  3123.             throw new \Exception('Error parsing Excel file: ' $e->getMessage());
  3124.         }
  3125.         return $locationData;
  3126.     }
  3127.     /**
  3128.      * Find column index by possible header names
  3129.      */
  3130.     private function findColumnIndex(array $headers, array $possibleNames): ?int
  3131.     {
  3132.         foreach ($possibleNames as $name) {
  3133.             $index array_search($name$headers);
  3134.             if ($index !== false) {
  3135.                 return $index;
  3136.             }
  3137.         }
  3138.         return null;
  3139.     }
  3140.     /**
  3141.      * @Route("/api/public/list-location-tags", name="list_location_tags_wso", methods={"POST"})
  3142.      */
  3143.     public function listLocationTags(Request $request): JsonResponse
  3144.     {
  3145.         try {
  3146.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  3147.             if ($response['success'] !== true) {
  3148.                 return $this->json($response);
  3149.             }
  3150.             $applicationName $request->headers->get('X-App-Name');
  3151.             $applicationConsumer $request->headers->get('X-App-Consumer-Key');
  3152.             if (!$applicationName || !$applicationConsumer) {
  3153.                 return $this->json(['success' => false,'message' => 'Missing required headers']);
  3154.             }
  3155.             $user DataObject\Customer::getByEmail($applicationNametrue);
  3156.             if (!$user) {
  3157.                 return $this->json([
  3158.                     'success' => false,
  3159.                     'message' => 'Invalid User'
  3160.                 ]);
  3161.             }
  3162.             $application DataObject\WSO2Applications::getByUser($usertrue);
  3163.             if (!$application) {
  3164.                 return $this->json([
  3165.                     'success' => false,
  3166.                     'message' => 'User not found'
  3167.                 ]);
  3168.             }
  3169.             // 5. Consumer key validation
  3170.             if ($application->getConsumerKey() !== $applicationConsumer) {
  3171.                 return $this->json([
  3172.                     'success' => false,
  3173.                     'message' => 'Invalid Consumer Key'
  3174.                 ]);
  3175.             }
  3176.             
  3177.             $params  json_decode($request->getContent(), true);
  3178.             $this->translator->setlocale($params['lang'] ?? DEFAULT_LOCALE);
  3179.             $result $this->locationModel->listTags($request$user$this->translator);
  3180.             return $this->json($result);
  3181.         } catch (\Exception $ex) {
  3182.             $this->logger->error($ex->getMessage());
  3183.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  3184.         }
  3185.     }
  3186.     /**
  3187.      * @Route("/api/public/create-tag", name="public-create-tag", methods={"POST"})
  3188.      */
  3189.     public function createTags(Request $request): JsonResponse
  3190.     {
  3191.         try {
  3192.             $response $this->publicUserPermissionService->isAuthorized($request$this->translator);
  3193.             if ($response['success'] !== true) {
  3194.                 return $this->json($response);
  3195.             }
  3196.             $applicationName $request->headers->get('X-App-Name');
  3197.             $applicationConsumer $request->headers->get('X-App-Consumer-Key');
  3198.             if (!$applicationName || !$applicationConsumer) {
  3199.                 return $this->json(['success' => false,'message' => 'Missing required headers']);
  3200.             }
  3201.             $user DataObject\Customer::getByEmail($applicationNametrue);
  3202.             if (!$user) {
  3203.                 return $this->json([
  3204.                     'success' => false,
  3205.                     'message' => 'Invalid User'
  3206.                 ]);
  3207.             }
  3208.             $application DataObject\WSO2Applications::getByUser($usertrue);
  3209.             if (!$application) {
  3210.                 return $this->json([
  3211.                     'success' => false,
  3212.                     'message' => 'User not found'
  3213.                 ]);
  3214.             }
  3215.             // 5. Consumer key validation
  3216.             if ($application->getConsumerKey() !== $applicationConsumer) {
  3217.                 return $this->json([
  3218.                     'success' => false,
  3219.                     'message' => 'Invalid Consumer Key'
  3220.                 ]);
  3221.             }
  3222.             $params  json_decode($request->getContent(), true);
  3223.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  3224.             $requiredParameters = ['tag_name'];
  3225.             foreach ($requiredParameters as $param) {
  3226.                 if (!isset($params[$param])) {
  3227.                     $missingParams[] = $param;
  3228.                 }
  3229.             }
  3230.             if (!empty($missingParams)) {
  3231.                 $parameterList implode(", "$missingParams);
  3232.                 return $this->json(['success' => false'message' => sprintf($this->translator->trans("missing_required_parameters: %s"), $parameterList)]);
  3233.             }
  3234.             $result $this->locationModel->createTags($request$params$user$this->translator);
  3235.             return $this->json($result);
  3236.         } catch (\Exception $ex) {
  3237.             $this->logger->error($ex->getMessage());
  3238.             return $this->json(['success' => false'message' => $this->translator->trans(USER_ERROR_MESSAGE)]);
  3239.         }
  3240.     }
  3241. /**
  3242.      * @Route("/api/public/list-reports", name="public-reports-listing")
  3243.      */
  3244.     public function getReportsListAction(Request $requestPaginatorInterface $paginator)
  3245.     {
  3246.         try {
  3247.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  3248.             if ($permissions['success'] !== true) {
  3249.                 return $this->json($permissions);
  3250.             }
  3251.             $user $permissions['user'];
  3252.             $params  json_decode($request->getContent(), true);
  3253.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  3254.             if (!isset($params['page']) || !isset($params['limit'])) {
  3255.                 throw new \Exception('Missing required params: page or limit');
  3256.             }
  3257.             $report_type = isset($params['report_type']) ? $params['report_type'] : null;
  3258.             $lang = isset($params['lang']) ? $params['lang'] : DEFAULT_LOCALE;
  3259.             $isPublicReports = isset($params['publicReports']) ? $params['publicReports'] : false// sending this flag from public portal to get all reports accordong to our needs
  3260.             $page $params['page'];
  3261.             $limit $params['limit'];
  3262.             $result $this->reportingPortalModel->getListReport($params);
  3263.             return $this->json($result);
  3264.         } catch (\Exception $ex) {
  3265.             $this->logger->error($ex->getMessage());
  3266.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  3267.         }
  3268.     }
  3269.     /**
  3270.      * @Route("/api/public/report/{url}", name="public-report-url")
  3271.      */
  3272.     public function getReportByUrlAction(Request $requestPaginatorInterface $paginator)
  3273.     {
  3274.         try {
  3275.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  3276.            
  3277.             if ($permissions['success'] !== true) {
  3278.                 return $this->json($permissions);
  3279.             }
  3280.             $user $permissions['user'];
  3281.             $url $request->get('url');
  3282.             $reportTypes = [
  3283.                 "red-sea-report",
  3284.                 "arabian-gulf-report",
  3285.                 "10-day-forecast-report",
  3286.                 "mashaer-weather-report",
  3287.                 "custom-weather-report",
  3288.                 "advance-custom-weather-report"
  3289.             ];
  3290.             $params  json_decode($request->getContent(), true);
  3291.             if (!in_array($url$reportTypes)) {
  3292.                 return $this->json(['success' => false'message' => 'Invalid report type']);
  3293.             }else{
  3294.                 $params['report_type'] =[
  3295.                     $url
  3296.                   ];
  3297.             }
  3298.             $this->translator->setlocale(isset($params["lang"]) ? $params["lang"] : DEFAULT_LOCALE);
  3299.             if (!isset($params['page']) || !isset($params['limit'])) {
  3300.                 throw new \Exception('Missing required params: page or limit');
  3301.             }
  3302.             // For red-sea-report, arabian-gulf-report, 10-day-forecast-report, mashaer-weather-report, custom-weather-report and advance-custom-weather-report: filter by id (query or body) so only nested report items with this id are shown
  3303.             $filterId null;
  3304.             if (in_array($url, ['red-sea-report''arabian-gulf-report''10-day-forecast-report''mashaer-weather-report''custom-weather-report''advance-custom-weather-report'], true)) {
  3305.                 $filterId $request->query->get('id');
  3306.                 if (null === $filterId && isset($params['id'])) {
  3307.                     $filterId $params['id'];
  3308.                 }
  3309.             }
  3310.             $result $this->reportingPortalModel->getListReport($params);
  3311.             if (in_array($url, ['red-sea-report''arabian-gulf-report''10-day-forecast-report''mashaer-weather-report''custom-weather-report''advance-custom-weather-report'], true) && $filterId !== null) {
  3312.                 $filteredData = [];
  3313.                 if (isset($result['data']) && is_array($result['data'])) {
  3314.                     foreach ($result['data'] as $mainDataItem) {
  3315.                         if (isset($mainDataItem['report']) && is_array($mainDataItem['report'])) {
  3316.                             $filteredReports array_filter($mainDataItem['report'], function ($reportItem) use ($filterId) {
  3317.                                 return isset($reportItem['id']) && (string) $reportItem['id'] === (string) $filterId;
  3318.                             });
  3319.                             $mainDataItem['report'] = array_values($filteredReports);
  3320.                         }
  3321.                         if (!empty($mainDataItem['report'])) {
  3322.                             $filteredData[] = $mainDataItem;
  3323.                         }
  3324.                     }
  3325.                 }
  3326.                 $result['data'] = $filteredData;
  3327.             }
  3328.             return $this->json($result);
  3329.         } catch (\Exception $ex) {
  3330.             $this->logger->error($ex->getMessage());
  3331.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  3332.         }
  3333.     }
  3334.     /**
  3335.      * @param list<array<string, mixed>> $jsonData
  3336.      * @param list<mixed> $filterByNames
  3337.      * @return list<array<string, mixed>>
  3338.      */
  3339.     private function filterJsonDataByNames(array $jsonData, array $filterByNames): array
  3340.     {
  3341.         $needles = [];
  3342.         foreach ($filterByNames as $name) {
  3343.             if (!is_string($name) && !is_numeric($name)) {
  3344.                 continue;
  3345.             }
  3346.             $trimmed trim((string) $name);
  3347.             if ($trimmed !== '') {
  3348.                 $needles[] = mb_strtolower($trimmed);
  3349.             }
  3350.         }
  3351.         if ($needles === []) {
  3352.             return $jsonData;
  3353.         }
  3354.         $filteredJsonData = [];
  3355.         foreach ($jsonData as $cityData) {
  3356.             if (!is_array($cityData)) {
  3357.                 continue;
  3358.             }
  3359.             $haystacks = [
  3360.                 $cityData['cityEn'] ?? '',
  3361.                 $cityData['cityAr'] ?? '',
  3362.                 $cityData['governorateEn'] ?? '',
  3363.                 $cityData['governorateAr'] ?? '',
  3364.             ];
  3365.             $matched false;
  3366.             foreach ($needles as $needle) {
  3367.                 foreach ($haystacks as $haystack) {
  3368.                     $haystackLower mb_strtolower((string) $haystack);
  3369.                     if ($haystackLower !== '' && mb_strpos($haystackLower$needle) !== false) {
  3370.                         $matched true;
  3371.                         break 2;
  3372.                     }
  3373.                 }
  3374.             }
  3375.             if ($matched) {
  3376.                 $filteredJsonData[] = $cityData;
  3377.             }
  3378.         }
  3379.         return $filteredJsonData;
  3380.     }
  3381.     /**
  3382.      * @param string ...$keys Request parameter keys to check (first match wins)
  3383.      */
  3384.     private function parseRequestBoolParam(array $paramsbool $defaultstring ...$keys): bool
  3385.     {
  3386.         foreach ($keys as $key) {
  3387.             if (!array_key_exists($key$params)) {
  3388.                 continue;
  3389.             }
  3390.             $value $params[$key];
  3391.             if (is_bool($value)) {
  3392.                 return $value;
  3393.             }
  3394.             if (is_int($value) || is_float($value)) {
  3395.                 return (int) $value === 1;
  3396.             }
  3397.             if (is_string($value)) {
  3398.                 $normalized strtolower(trim($value));
  3399.                 if (in_array($normalized, ['1''true''yes''on'], true)) {
  3400.                     return true;
  3401.                 }
  3402.                 if (in_array($normalized, ['0''false''no''off'], true)) {
  3403.                     return false;
  3404.                 }
  3405.             }
  3406.         }
  3407.         return $default;
  3408.     }
  3409.     /**
  3410.      * @Route("/api/public/get-marine-report", name="get-marine-report")
  3411.      */
  3412.     public function getPortWeatherListAction(Request $requestPaginatorInterface $paginator)
  3413.     {
  3414.         try {
  3415.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  3416.             if ($permissions['success'] !== true) {
  3417.                 return $this->json($permissions);
  3418.             }
  3419.             return $this->json($this->marineReportModel->getMarineReport());
  3420.         } catch (\Exception $ex) {
  3421.             $this->logger->error($ex->getMessage());
  3422.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  3423.         }
  3424.     }
  3425.     /**
  3426.      * @Route("/api/ncm/marine/get-marine-report", name="get-marine-report-user")
  3427.      */
  3428.     public function getPortWeatherListUserAction(Request $request)
  3429.     {
  3430.         try {
  3431.             return $this->json($this->marineReportModel->getMarineReport());
  3432.         } catch (\Exception $ex) {
  3433.             $this->logger->error($ex->getMessage());
  3434.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  3435.         }
  3436.     }
  3437.     /**
  3438.      * @Route("/api/public/get-marine-description", name="get-marine-description")
  3439.      */
  3440.     public function getMarineDescriptionAction(Request $request)
  3441.     {
  3442.         try {
  3443.             $permissions $this->publicUserPermissionService->isAuthorized($request$this->translator);
  3444.             if ($permissions['success'] !== true) {
  3445.                 return $this->json($permissions);
  3446.             }
  3447.             return $this->json($this->marineReportModel->getMarineDescription($this->marineTranslator));
  3448.         } catch (\Exception $ex) {
  3449.             $this->logger->error($ex->getMessage());
  3450.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  3451.         }
  3452.     }
  3453.     /**
  3454.      * @Route("/api/ncm/marine/get-marine-description", name="get-marine-description-user")
  3455.      */
  3456.     public function getMarineDescriptionUserAction(Request $request)
  3457.     {
  3458.         try {
  3459.             return $this->json($this->marineReportModel->getMarineDescription($this->marineTranslator));
  3460.         } catch (\Exception $ex) {
  3461.             $this->logger->error($ex->getMessage());
  3462.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  3463.         }
  3464.     }
  3465.     /**
  3466.      * Combined marine report + description. Fully public: no API-key and no
  3467.      * user token (the /api/public path is PUBLIC_ACCESS and no isAuthorized()
  3468.      * check is performed).
  3469.      *
  3470.      * @Route("/api/public/get-marine-forecast", name="get-marine-forecast")
  3471.      */
  3472.     public function getMarineForecastAction(Request $request)
  3473.     {
  3474.         try {
  3475.             return $this->json([
  3476.                 'success' => true,
  3477.                 'data'    => $this->marineReportModel->getMarineForecast($this->marineTranslator),
  3478.             ]);
  3479.         } catch (\Exception $ex) {
  3480.             $this->logger->error($ex->getMessage());
  3481.             return $this->json(['success' => false'message' => $ex->getMessage()]);
  3482.         }
  3483.     }
  3484. }