<?php

namespace App\Console\Commands;

use App\Services\MeilisearchService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Mtc\MercuryDataModels\Vehicle;

class PruneVehiclesFromMeilisearch extends Command
{
    protected $signature = 'meilisearch:prune-vehicles
                            {--batch-size=1000 : Number of index documents to scan per page}
                            {--delete-batch-size=1000 : Number of documents to remove per delete request}
                            {--dry-run : Report orphaned documents without removing them}';

    protected $description = 'Remove Meilisearch vehicle documents that no longer match an active vehicle in the database';

    public function handle(MeilisearchService $meilisearch): int
    {
        $batchSize = max(1, (int) $this->option('batch-size'));
        $deleteBatchSize = max(1, (int) $this->option('delete-batch-size'));
        $dryRun = (bool) $this->option('dry-run');

        $indexName = $meilisearch->getIndexName();
        $this->info("Scanning Meilisearch index {$indexName} for orphaned vehicle documents...");

        try {
            $orphanIds = $this->collectOrphanIds($meilisearch, $batchSize);
        } catch (\Exception $e) {
            // A missing index (e.g. Meilisearch never enabled for this tenant) should not
            // fail a `tenants:run` sweep across every tenant.
            $this->warn("Could not scan index {$indexName}: {$e->getMessage()}");
            Log::warning('Meilisearch: Prune skipped, could not scan index', [
                'index' => $indexName,
                'error' => $e->getMessage(),
            ]);

            return self::SUCCESS;
        }

        $orphanCount = count($orphanIds);
        $this->info("Found {$orphanCount} orphaned document(s).");

        if ($orphanCount === 0) {
            return self::SUCCESS;
        }

        if ($dryRun) {
            $this->warn("Dry run: {$orphanCount} document(s) would be removed. No changes made.");

            return self::SUCCESS;
        }

        $removed = $this->removeOrphans($meilisearch, $orphanIds, $deleteBatchSize);

        $this->info("Removed {$removed} orphaned document(s) from {$indexName}.");
        Log::info('Meilisearch: Pruned orphaned vehicle documents', [
            'index' => $indexName,
            'removed' => $removed,
        ]);

        return self::SUCCESS;
    }

    /**
     * Page through every document ID in the index, collecting those that do not
     * correspond to an active vehicle in the database.
     *
     * The scan does not delete while paging so that offsets stay stable; removal
     * happens afterwards in {@see removeOrphans()}.
     *
     * @return int[]
     */
    private function collectOrphanIds(MeilisearchService $meilisearch, int $batchSize): array
    {
        $offset = 0;
        $scanned = 0;
        $orphanIds = [];

        do {
            $indexedIds = $meilisearch->getIndexedVehicleIds($offset, $batchSize);
            $count = count($indexedIds);

            if ($count === 0) {
                break;
            }

            $existingIds = Vehicle::query()
                ->active()
                ->whereIn('id', $indexedIds)
                ->pluck('id')
                ->all();

            foreach (array_diff($indexedIds, $existingIds) as $orphanId) {
                $orphanIds[] = $orphanId;
            }

            $scanned += $count;
            $offset += $count;
        } while ($count === $batchSize);

        $this->info("Scanned {$scanned} document(s).");

        return $orphanIds;
    }

    /**
     * Remove the collected orphan IDs from the index using batched delete requests.
     *
     * @param int[] $orphanIds
     */
    private function removeOrphans(MeilisearchService $meilisearch, array $orphanIds, int $deleteBatchSize): int
    {
        $removed = 0;

        foreach (array_chunk($orphanIds, $deleteBatchSize) as $chunk) {
            try {
                $meilisearch->deleteVehicles($chunk);
                $removed += count($chunk);
            } catch (\Exception $e) {
                $this->error("Failed to remove a batch of documents: {$e->getMessage()}");
                Log::error('Meilisearch: Failed to remove orphaned vehicle batch', [
                    'error' => $e->getMessage(),
                ]);
            }
        }

        return $removed;
    }
}
