<?php

namespace App\Services;

use App\Facades\Feature;
use Carbon\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Meilisearch\Client;
use Meilisearch\Endpoints\Indexes;
use Mtc\MercuryDataModels\Vehicle;

class MeilisearchService
{
    private ?Client $client = null;

    public const VEHICLE_RELATIONS = [
        'make',
        'model',
        'bodyStyle',
        'transmission',
        'fuelType',
        'dealership.franchise',
        'labels',
        'features',
        'attributeValues',
        'equipment',
    ];

    public function isEnabled(): bool
    {
        return Feature::isEnabled('meilisearch-vehicle-filter');
    }

    public function getClient(): Client
    {
        if ($this->client === null) {
            $this->client = new Client(
                Config::get('meilisearch.host'),
                Config::get('meilisearch.key')
            );
        }

        return $this->client;
    }

    public function getVehicleIndex(): Indexes
    {
        return $this->getClient()->index($this->getIndexName());
    }

    public function getIndexName(): string
    {
        return tenant('id') . '_vehicles';
    }

    /**
     * Index a single vehicle in Meilisearch.
     * If the vehicle is unpublished or soft-deleted, it is removed from the index instead.
     */
    public function indexVehicle(Vehicle $vehicle): void
    {
        if (!$this->isEnabled()) {
            return;
        }

        if (!$vehicle->is_published || $vehicle->trashed()) {
            $this->deleteVehicle($vehicle->id);
            return;
        }

        try {
            $vehicle->loadMissing(self::VEHICLE_RELATIONS);

            $document = $this->transformVehicle($vehicle);
            $this->getVehicleIndex()->addDocuments([$document]);

            Log::debug('Meilisearch: Indexed vehicle', ['vehicle_id' => $vehicle->id]);
        } catch (\Exception $e) {
            Log::error('Meilisearch: Failed to index vehicle', [
                'vehicle_id' => $vehicle->id,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Index multiple vehicles in Meilisearch.
     */
    public function indexVehicles(iterable $vehicles): void
    {
        if (!$this->isEnabled()) {
            return;
        }

        try {
            $documents = collect($vehicles)->map(fn($vehicle) => $this->transformVehicle($vehicle))->toArray();

            if (!empty($documents)) {
                $this->getVehicleIndex()->addDocuments($documents);
                Log::debug('Meilisearch: Indexed vehicles batch', ['count' => count($documents)]);
            }
        } catch (\Exception $e) {
            Log::error('Meilisearch: Failed to index vehicles batch', [
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Delete a vehicle from Meilisearch index.
     */
    public function deleteVehicle(int $vehicleId): void
    {
        if (!$this->isEnabled()) {
            return;
        }

        try {
            $this->getVehicleIndex()->deleteDocument($vehicleId);
            Log::debug('Meilisearch: Deleted vehicle', ['vehicle_id' => $vehicleId]);
        } catch (\Exception $e) {
            Log::error('Meilisearch: Failed to delete vehicle', [
                'vehicle_id' => $vehicleId,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Delete all documents from the vehicle index.
     */
    public function deleteAllDocuments(): void
    {
        $this->getVehicleIndex()->deleteAllDocuments();
    }

    /**
     * Remove recently soft-deleted vehicles from the Meilisearch index.
     */
    public function removeRecentlyDeletedVehicles($hours = 24): void
    {
        $deletedIds = Vehicle::onlyTrashed()
            ->where('deleted_at', '>=', Carbon::now()->subHours($hours))
            ->pluck('id')
            ->toArray();

        if (empty($deletedIds)) {
            return;
        }

        try {
            $this->getVehicleIndex()->deleteDocuments($deletedIds);
            Log::info('Meilisearch: Removed soft-deleted vehicles from index', ['count' => count($deletedIds)]);
        } catch (\Exception $e) {
            Log::error('Meilisearch: Failed to remove soft-deleted vehicles', ['error' => $e->getMessage()]);
        }
    }

    /**
     * Remove recently unpublished vehicles from the Meilisearch index.
     */
    public function removeRecentlyUnpublishedVehicles(int $hours = 24): void
    {
        $unpublishedIds = Vehicle::withTrashed()
            ->where('is_published', 0)
            ->where('updated_at', '>=', Carbon::now()->subHours($hours))
            ->pluck('id')
            ->toArray();

        if (empty($unpublishedIds)) {
            return;
        }

        try {
            $this->getVehicleIndex()->deleteDocuments($unpublishedIds);
            Log::info('Meilisearch: Removed unpublished vehicles from index', ['count' => count($unpublishedIds)]);
        } catch (\Exception $e) {
            Log::error('Meilisearch: Failed to remove unpublished vehicles', ['error' => $e->getMessage()]);
        }
    }

    /**
     * Sync all vehicles to Meilisearch (full reindex).
     */
    public function syncAllVehicles(int $batchSize = 500): void
    {
        if (!$this->isEnabled()) {
            return;
        }

        $this->configureIndex();

        $index = $this->getVehicleIndex();
        $this->removeRecentlyDeletedVehicles();
        $this->removeRecentlyUnpublishedVehicles();

        Vehicle::query()
            ->where('is_published', 1)
            ->with(self::VEHICLE_RELATIONS)
            ->chunk($batchSize, function ($vehicles) use ($index) {
                $documents = $vehicles->map(fn(Vehicle $vehicle) => $this->transformVehicle($vehicle))->toArray();

                try {
                    $index->addDocuments($documents);
                } catch (\Exception $e) {
                    Log::error('Meilisearch: Error syncing batch', ['error' => $e->getMessage()]);
                }
            });

        Log::info('Meilisearch: Full vehicle sync completed', ['tenant' => tenant('id')]);
    }

    /**
     * Configure the Meilisearch index with proper settings.
     */
    public function configureIndex(): void
    {
        $client = $this->getClient();
        $indexName = $this->getIndexName();

        try {
            $client->createIndex($indexName, ['primaryKey' => 'id']);
        } catch (\Exception $e) {
            // Index may already exist
        }

        $index = $client->index($indexName);

        $index->updateFilterableAttributes($this->getFilterableAttributes());
        $index->updateSortableAttributes($this->getSortableAttributes());
        $index->updateSearchableAttributes($this->getSearchableAttributes());
        $index->updatePagination(['maxTotalHits' => 1_000_000]);
    }

    /**
     * Transform a Vehicle model to a Meilisearch document.
     */
    public function transformVehicle(Vehicle $vehicle): array
    {
        $stockStatus = $vehicle->attributeValues
            ->where('slug', 'stock_status')
            ->first()?->value;

        return [
            'id' => $vehicle->id,
            'tenant_id' => tenant('id'),
            'title' => $vehicle->title,
            'derivative' => $vehicle->derivative,
            'registration_number' => $vehicle->registration_number,
            'slug' => $vehicle->slug,

            // Make
            'make_id' => $vehicle->make_id,
            'make_name' => $vehicle->make?->name,
            'make_slug' => $vehicle->make?->slug,

            // Model
            'model_id' => $vehicle->model_id,
            'model_name' => $vehicle->model?->name,
            'model_slug' => $vehicle->model?->slug,

            // Body style
            'body_style_id' => $vehicle->body_style_id,
            'body_style_slug' => $vehicle->bodyStyle?->slug,

            // Transmission
            'transmission_id' => $vehicle->transmission_id,
            'transmission_slug' => $vehicle->transmission?->slug,

            // Fuel type
            'fuel_type_id' => $vehicle->fuel_type_id,
            'fuel_type_slug' => $vehicle->fuelType?->slug,

            // Drivetrain
            'drivetrain_id' => $vehicle->drivetrain_id,

            // Dealership
            'dealership_id' => $vehicle->dealership_id,
            'dealership_slug' => $vehicle->dealership?->slug,

            // Franchise
            'franchise_id' => $vehicle->dealership?->franchise_id,

            // Colour
            'colour' => $vehicle->colour,
            'colour_slug' => $vehicle->colour ? Str::slug($vehicle->colour) : null,

            // Status flags
            'is_new' => $vehicle->is_new,
            'is_published' => $vehicle->is_published,
            'is_sold' => $vehicle->is_sold,
            'is_reserved' => $vehicle->is_reserved,
            'is_demo' => $vehicle->is_demo,
            'is_vat_applicable' => $vehicle->is_vat_applicable,

            // Type
            'type' => $vehicle->type,
            'vehicle_category' => $vehicle->vehicle_category ?? null,

            // Pricing
            'price' => (float) $vehicle->price,
            'monthly_price' => (float) $vehicle->monthly_price,
            'original_price' => $vehicle->original_price ? (float) $vehicle->original_price : null,

            // Technical specs
            'manufacture_year' => (int) $vehicle->manufacture_year,
            'odometer_km' => (int) $vehicle->odometer_km,
            'odometer_mi' => (int) $vehicle->odometer_mi,
            'engine_size_cc' => (int) $vehicle->engine_size_cc,
            'door_count' => (int) $vehicle->door_count,
            'seat_count' => (int) $vehicle->seats,
            'co2' => (int) $vehicle->co2,
            'mpg' => (int) $vehicle->mpg,

            // Labels and features
            'label_ids' => $vehicle->labels->pluck('id')->toArray(),
            'feature_ids' => $vehicle->features->pluck('id')->toArray(),

            // Stock status
            'stock_status' => $stockStatus,

            // Trim
            'trim' => $vehicle->trim,
            'trim_slug' => $vehicle->trim ? Str::slug($vehicle->trim) : null,

            // Description
            'description' => $vehicle->description,

            // Equipment
            'equipment' => $vehicle->equipment->pluck('description')->filter()->implode(', '),

            // Timestamps
            'created_at' => $vehicle->created_at?->timestamp,
            'published_at' => $vehicle->published_at?->timestamp,
            'sold_at' => $vehicle->sold_at ? Carbon::parse($vehicle->sold_at)->timestamp : null,

            // Geolocation for distance filtering
            '_geo' => $vehicle->lat && $vehicle->lng ? [
                'lat' => (float) $vehicle->lat,
                'lng' => (float) $vehicle->lng,
            ] : null,
        ];
    }

    private function getFilterableAttributes(): array
    {
        return [
            'tenant_id',
            'make_id',
            'make_slug',
            'model_id',
            'model_slug',
            'body_style_id',
            'body_style_slug',
            'transmission_id',
            'transmission_slug',
            'fuel_type_id',
            'fuel_type_slug',
            'drivetrain_id',
            'dealership_id',
            'dealership_slug',
            'franchise_id',
            'colour',
            'colour_slug',
            'is_new',
            'is_published',
            'is_sold',
            'is_reserved',
            'is_demo',
            'is_vat_applicable',
            'type',
            'vehicle_category',
            'price',
            'mpg',
            'monthly_price',
            'manufacture_year',
            'odometer_km',
            'odometer_mi',
            'engine_size_cc',
            'door_count',
            'seat_count',
            'co2',
            'label_ids',
            'feature_ids',
            'stock_status',
            'trim_slug',
            'sold_at',
            '_geo',
        ];
    }

    private function getSortableAttributes(): array
    {
        return [
            'created_at',
            'price',
            'monthly_price',
            'manufacture_year',
            'odometer_km',
            'odometer_mi',
            'sold_at',
        ];
    }

    private function getSearchableAttributes(): array
    {
        return [
            'title',
            'derivative',
            'registration_number',
            'make_name',
            'model_name',
            'colour',
            'trim',
            'description',
            'equipment',
        ];
    }
}
