<?php

namespace Tests\Tenant;

use App\Http\Controllers\NotificationMapController;
use App\Jobs\ProcessPendingProviderNotifications;
use App\Models\NotificationMap;
use App\NotificationMaps\NotificationPayloadMapper;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Bus;
use Illuminate\Validation\ValidationException;
use Mtc\MercuryDataModels\PendingProviderNotification;
use Mtc\MercuryDataModels\Vehicle;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Tests\TenantTestCase;

class NotificationMapEndpointTest extends TenantTestCase
{
    private function controller(): NotificationMapController
    {
        return new NotificationMapController(new NotificationPayloadMapper());
    }

    private function makeMap(bool $active = true): NotificationMap
    {
        return NotificationMap::create([
            'name' => 'Partner Feed',
            'type' => 'vehicle',
            'active' => $active,
            'field_map' => [
                ['source' => 'stock_id', 'destination' => 'uuid', 'type' => 'string'],
                ['source' => 'vehicle_type', 'destination' => 'type', 'type' => 'string'],
                ['source' => 'reg', 'destination' => 'registration_number', 'type' => 'string'],
                ['source' => 'price', 'destination' => 'price', 'type' => 'float'],
                ['source' => 'mileage', 'destination' => 'odometer_mi', 'type' => 'integer'],
            ],
            'validation' => [
                'uuid' => ['required', 'string'],
                'type' => ['required', 'string'],
            ],
        ]);
    }

    private function request(array $body): Request
    {
        return Request::create('/api/notifications/tenant/partner-feed', 'POST', $body);
    }

    private function validBody(string $stockId = 'STK-1', float $price = 19995): array
    {
        return [
            'stock_id' => $stockId,
            'vehicle_type' => 'car',
            'reg' => 'AB12CDE',
            'price' => $price,
            'mileage' => 15000,
        ];
    }

    public function testUnknownSlugReturns404(): void
    {
        $this->expectException(NotFoundHttpException::class);

        $this->controller()->store($this->request($this->validBody()), 'does-not-exist');
    }

    public function testInactiveMapReturns404(): void
    {
        $this->makeMap(active: false);

        $this->expectException(NotFoundHttpException::class);

        $this->controller()->store($this->request($this->validBody()), 'partner-feed');
    }

    public function testMissingRequiredFieldReturns422(): void
    {
        $this->makeMap();

        $this->expectException(ValidationException::class);

        // No stock_id, so the required uuid rule must fail.
        $this->controller()->store($this->request(['vehicle_type' => 'car', 'reg' => 'AB12CDE']), 'partner-feed');
    }

    public function testValidPayloadCapturesAndDispatchesDrain(): void
    {
        Bus::fake();
        $map = $this->makeMap();

        $response = $this->controller()->store($this->request($this->validBody()), 'partner-feed');

        $this->assertSame('received', $response->getData()->status);

        $row = PendingProviderNotification::query()->where('provider', $map->providerKey())->first();
        $this->assertNotNull($row);
        $this->assertSame('STK-1', $row->reference);
        $this->assertSame('STK-1', $row->payload['stock_id']);

        Bus::assertDispatched(ProcessPendingProviderNotifications::class);
    }

    public function testDrainUpsertsVehicleFromCapturedPayload(): void
    {
        $map = $this->makeMap();

        PendingProviderNotification::capture($map->providerKey(), 'STK-1', $this->validBody('STK-1', 19995));
        (new ProcessPendingProviderNotifications($map->providerKey()))->handle();

        $vehicle = Vehicle::query()->where('stock_provider', $map->providerKey())->where('uuid', 'STK-1')->first();
        $this->assertNotNull($vehicle);
        // The vehicle model normalises the registration (e.g. inserts a space), so
        // compare with whitespace stripped rather than against the raw input.
        $this->assertSame('AB12CDE', str_replace(' ', '', (string) $vehicle->registration_number));
        $this->assertSame(19995.0, (float) $vehicle->price);
        $this->assertSame(15000, (int) $vehicle->odometer_mi);

        // Backlog cleared after drain.
        $this->assertSame(0, PendingProviderNotification::query()->where('provider', $map->providerKey())->count());
    }

    public function testDrainUpdatesExistingVehicleRatherThanDuplicating(): void
    {
        $map = $this->makeMap();

        PendingProviderNotification::capture($map->providerKey(), 'STK-1', $this->validBody('STK-1', 19995));
        (new ProcessPendingProviderNotifications($map->providerKey()))->handle();

        PendingProviderNotification::capture($map->providerKey(), 'STK-1', $this->validBody('STK-1', 17995));
        (new ProcessPendingProviderNotifications($map->providerKey()))->handle();

        $vehicles = Vehicle::query()->where('stock_provider', $map->providerKey())->where('uuid', 'STK-1')->get();
        $this->assertCount(1, $vehicles);
        $this->assertSame(17995.0, (float) $vehicles->first()->price);
    }
}
