addOption('limit', null, InputOption::VALUE_REQUIRED, 'Maximum number of snapshots to process', '500') ->addOption('force', 'f', InputOption::VALUE_NONE, 'Refresh even if snapshot was refreshed recently') ->addOption('refresh-after', null, InputOption::VALUE_REQUIRED, 'Minimum minutes since last refresh before a snapshot is eligible (ignored with --force)', '360') ; } /** * Executes snapshot refresh and purge flow. */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); if (false === $this->lock('bpn_xml_sync')) { $this->logger->info('Snapshot refresh skipped: xml-sync is running'); return Command::SUCCESS; } $limit = (int) $input->getOption('limit'); $force = true === $input->getOption('force'); $refreshAfterMinutes = (int) $input->getOption('refresh-after'); $xmlFileMap = null; try { $xmlFileMap = $this->travelLoader->generateFilesMap(); } catch (\Throwable $e) { $this->logger->warning('Failed to load XML files map for snapshot refresh filtering; skipping all candidates', [ 'error' => $e->getMessage(), ]); } // Phase 2 — enrich non-XML-backed snapshots via extended-availability API $xmlDateIds = null !== $xmlFileMap ? array_keys($xmlFileMap) : null; $result = $this->snapshotService->refreshExtendedSnapshots($limit, $force, $refreshAfterMinutes, $xmlDateIds); $io->success(sprintf( 'Snapshot refresh complete: %d processed, %d updated, %d failed', $result['processed'], $result['updated'], $result['failed'] )); $this->logger->info('Travel snapshot refresh finished', $result); $deleted = $this->snapshotService->purgeExpiredSnapshots(); $io->note(sprintf('Deleted %d expired snapshots.', $deleted)); return Command::SUCCESS; } }