<?php

namespace Tests\Tenant;

use App\BookingRepository;
use App\TaxonomyMap;
use Illuminate\Support\Facades\App;
use Mtc\MercuryDataModels\Booking;
use Mtc\MercuryDataModels\ServicePackage;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\VehicleLookup\VehicleLookupService;
use Ramsey\Uuid\Uuid;
use Tests\DatabaseTestCase;

class ServiceBookingPackageFilterTest extends DatabaseTestCase
{
    private const PROVIDER = 'auto-trader';
    private const FORD_MAKE_ID = 1;
    private const BMW_MAKE_ID = 2;

    public function testReturnsUniversalAndMatchingPackagesForBookingMake(): void
    {
        $this->forceLookupProvider(self::PROVIDER);
        $this->mapMake('Ford', self::FORD_MAKE_ID);

        $universal = $this->createPackage('Universal Service', null);
        $fordOnly = $this->createPackage('Ford Tyres', self::FORD_MAKE_ID);
        $this->createPackage('BMW Tyres', self::BMW_MAKE_ID);

        $booking = $this->createBookingWithMake('Ford');

        $packages = App::make(BookingRepository::class)->getPackages($booking);

        $this->assertEqualsCanonicalizing(
            [$universal->id, $fordOnly->id],
            $packages->pluck('id')->all()
        );
    }

    public function testExactVehicleMakeMatchIsPreferredOverTaxonomyMap(): void
    {
        $this->forceLookupProvider(self::PROVIDER);
        $this->createVehicleMake(self::FORD_MAKE_ID, 'Ford');
        $this->mapMake('Ford', self::BMW_MAKE_ID);

        $universal = $this->createPackage('Universal Service', null);
        $fordOnly = $this->createPackage('Ford Tyres', self::FORD_MAKE_ID);
        $this->createPackage('BMW Tyres', self::BMW_MAKE_ID);

        $booking = $this->createBookingWithMake('Ford');

        $packages = App::make(BookingRepository::class)->getPackages($booking);

        $this->assertEqualsCanonicalizing(
            [$universal->id, $fordOnly->id],
            $packages->pluck('id')->all()
        );
    }

    public function testFallsBackToTaxonomyMapWhenNoExactVehicleMakeMatch(): void
    {
        $this->forceLookupProvider(self::PROVIDER);
        $this->createVehicleMake(self::FORD_MAKE_ID, 'Ford');
        $this->mapMake('FORD MOTOR CO', self::FORD_MAKE_ID);

        $universal = $this->createPackage('Universal Service', null);
        $fordOnly = $this->createPackage('Ford Tyres', self::FORD_MAKE_ID);
        $this->createPackage('BMW Tyres', self::BMW_MAKE_ID);

        $booking = $this->createBookingWithMake('FORD MOTOR CO');

        $packages = App::make(BookingRepository::class)->getPackages($booking);

        $this->assertEqualsCanonicalizing(
            [$universal->id, $fordOnly->id],
            $packages->pluck('id')->all()
        );
    }

    public function testReturnsAllPackagesWhenBookingHasNoVehicleMake(): void
    {
        $this->forceLookupProvider(self::PROVIDER);

        $universal = $this->createPackage('Universal Service', null);
        $fordOnly = $this->createPackage('Ford Tyres', self::FORD_MAKE_ID);

        $booking = Booking::factory()->create([
            'uuid' => Uuid::uuid4()->toString(),
            'vehicle_data' => [],
        ]);

        $packages = App::make(BookingRepository::class)->getPackages($booking);

        $this->assertEqualsCanonicalizing(
            [$universal->id, $fordOnly->id],
            $packages->pluck('id')->all()
        );
    }

    public function testReturnsAllPackagesWhenMakeIsUnmapped(): void
    {
        $this->forceLookupProvider(self::PROVIDER);

        $universal = $this->createPackage('Universal Service', null);
        $fordOnly = $this->createPackage('Ford Tyres', self::FORD_MAKE_ID);

        $booking = $this->createBookingWithMake('UnknownMake');

        $packages = App::make(BookingRepository::class)->getPackages($booking);

        $this->assertEqualsCanonicalizing(
            [$universal->id, $fordOnly->id],
            $packages->pluck('id')->all()
        );
    }

    public function testReturnsAllPackagesWhenNoBookingProvided(): void
    {
        $universal = $this->createPackage('Universal Service', null);
        $fordOnly = $this->createPackage('Ford Tyres', self::FORD_MAKE_ID);

        $packages = App::make(BookingRepository::class)->getPackages();

        $this->assertEqualsCanonicalizing(
            [$universal->id, $fordOnly->id],
            $packages->pluck('id')->all()
        );
    }

    private function forceLookupProvider(string $provider): void
    {
        $this->app->singleton(VehicleLookupService::class);
        App::make(VehicleLookupService::class)->setDriver($provider);
    }

    private function mapMake(string $term, int $makeId): TaxonomyMap
    {
        return TaxonomyMap::query()->create([
            'provider' => self::PROVIDER,
            'taxonomy_type' => TaxonomyMap::MAKE,
            'term' => $term,
            'taxonomy_id' => $makeId,
        ]);
    }

    private function createPackage(string $name, ?int $applicableMakeId): ServicePackage
    {
        return ServicePackage::query()->create([
            'name' => $name,
            'applicable_make_id' => $applicableMakeId,
            'order' => 0,
            'price' => 100,
        ]);
    }

    private function createBookingWithMake(string $makeName): Booking
    {
        return Booking::factory()->create([
            'uuid' => Uuid::uuid4()->toString(),
            'vehicle_data' => ['make' => $makeName],
        ]);
    }

    private function createVehicleMake(int $id, string $name): VehicleMake
    {
        return VehicleMake::query()->create([
            'id' => $id,
            'name' => $name,
        ]);
    }
}
