<?php

namespace Tests\Tenant;

use App\Facades\Settings;
use App\Http\Controllers\AutoGrabNotificationController;
use App\Jobs\HandleAutoGrabNotification;
use App\Jobs\ProcessPendingProviderNotifications;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Bus;
use Mtc\MercuryDataModels\ApiNotification;
use Mtc\MercuryDataModels\PendingProviderNotification;
use Mtc\MercuryDataModels\Vehicle;
use Tests\Support\RecordingPendingProcessor;
use Tests\TenantTestCase;

class PendingProviderNotificationDrainTest extends TenantTestCase
{
    protected function setUp(): void
    {
        parent::setUp();
        RecordingPendingProcessor::reset();
        config(['pending-notifications.processors.autograb' => RecordingPendingProcessor::class]);
        Settings::update('stock-autograb-enabled', true);
    }

    public function testCaptureDeduplicatesByReferenceToLatestSnapshot(): void
    {
        PendingProviderNotification::capture('autograb', 'AB12CDE', $this->payload('AB12CDE', 1000), 'UPDATE');
        PendingProviderNotification::capture('autograb', 'AB12CDE', $this->payload('AB12CDE', 2000), 'UPDATE');

        $rows = PendingProviderNotification::query()->where('provider', 'autograb')->get();

        $this->assertCount(1, $rows);
        $this->assertSame(2000, $rows->first()->payload['lead']['price']);
        $this->assertNull($rows->first()->claimed_at);
    }

    public function testReferencelessNotificationsDoNotMerge(): void
    {
        PendingProviderNotification::capture('autograb', null, $this->payload(null, 1), null);
        PendingProviderNotification::capture('autograb', null, $this->payload(null, 2), null);

        $this->assertSame(2, PendingProviderNotification::query()->where('provider', 'autograb')->count());
    }

    public function testDrainerProcessesLatestPerReferenceThenClearsTheBacklog(): void
    {
        PendingProviderNotification::capture('autograb', 'AB12CDE', $this->payload('AB12CDE', 1000), 'UPDATE');
        PendingProviderNotification::capture('autograb', 'AB12CDE', $this->payload('AB12CDE', 2000), 'UPDATE');
        PendingProviderNotification::capture('autograb', 'ZZ99ZZZ', $this->payload('ZZ99ZZZ', 3000), 'UPDATE');

        (new ProcessPendingProviderNotifications('autograb'))->handle();

        // One payload per distinct reference, each the latest snapshot.
        $prices = collect(RecordingPendingProcessor::$received)
            ->map(fn ($p) => $p['lead']['price'])
            ->sort()
            ->values()
            ->all();
        $this->assertSame([2000, 3000], $prices);

        $this->assertSame(0, PendingProviderNotification::query()->where('provider', 'autograb')->count());
    }

    public function testDrainerSuppressesAuditingWhileProcessingAndRestoresAfter(): void
    {
        PendingProviderNotification::capture('autograb', 'AB12CDE', $this->payload('AB12CDE', 1000), 'UPDATE');

        $this->assertFalse(Vehicle::isAuditingDisabled());

        (new ProcessPendingProviderNotifications('autograb'))->handle();

        $this->assertNotEmpty(RecordingPendingProcessor::$auditingDisabled);
        $this->assertTrue(RecordingPendingProcessor::$auditingDisabled[0], 'Auditing should be off during processing');
        $this->assertFalse(Vehicle::isAuditingDisabled(), 'Auditing should be restored afterwards');
    }

    public function testDrainerDropsFailedRowAndRecordsFailureAudit(): void
    {
        PendingProviderNotification::capture('autograb', 'AB12CDE', $this->payload('AB12CDE', 1000), 'UPDATE');
        RecordingPendingProcessor::$throwNext = true;

        (new ProcessPendingProviderNotifications('autograb'))->handle();

        $this->assertSame(0, PendingProviderNotification::query()->where('provider', 'autograb')->count());
        $this->assertSame(
            1,
            ApiNotification::query()
                ->where('provider', 'autograb')
                ->where('data_model', 'pending-notification-failure')
                ->count()
        );
    }

    public function testDrainerIsUniquePerTenantAndProvider(): void
    {
        $job = new ProcessPendingProviderNotifications('autograb');

        $this->assertInstanceOf(ShouldBeUnique::class, $job);
        $this->assertIsString($job->uniqueId());
    }

    public function testControllerCapturesWithoutDispatchingPerWebhookAndDrainsAtThreshold(): void
    {
        Bus::fake();
        Settings::update('stock-autograb-batch-threshold', 2);

        $controller = new AutoGrabNotificationController();

        $controller->store($this->request('AB12CDE'));
        Bus::assertNotDispatched(ProcessPendingProviderNotifications::class);

        $controller->store($this->request('ZZ99ZZZ'));

        $this->assertSame(2, PendingProviderNotification::query()->where('provider', 'autograb')->count());
        Bus::assertNotDispatched(HandleAutoGrabNotification::class);
        Bus::assertDispatched(ProcessPendingProviderNotifications::class);
    }

    public function testControllerSkipsPrivateSellerLeads(): void
    {
        Bus::fake();

        $controller = new AutoGrabNotificationController();
        $controller->store($this->request('AB12CDE', 'private'));

        $this->assertSame(0, PendingProviderNotification::query()->where('provider', 'autograb')->count());
        Bus::assertNotDispatched(ProcessPendingProviderNotifications::class);
    }

    public function testControllerDiscardsReprocessEvents(): void
    {
        Bus::fake();

        $request = Request::create('/api/notifications/autograb', 'POST', [
            'event' => 'REPROCESS',
            'lead' => [
                'rego' => 'AB12CDE',
                'seller_type' => 'dealer',
                'price' => 1000,
            ],
        ]);

        (new AutoGrabNotificationController())->store($request);

        $this->assertSame(0, PendingProviderNotification::query()->where('provider', 'autograb')->count());
        Bus::assertNotDispatched(ProcessPendingProviderNotifications::class);
    }

    private function payload(?string $rego, int $price): array
    {
        return [
            'event' => 'UPDATE',
            'lead' => [
                'rego' => $rego,
                'seller_type' => 'dealer',
                'price' => $price,
            ],
        ];
    }

    private function request(string $rego, string $sellerType = 'dealer'): Request
    {
        return Request::create('/api/notifications/autograb', 'POST', [
            'event' => 'UPDATE',
            'lead' => [
                'rego' => $rego,
                'seller_type' => $sellerType,
                'price' => 1000,
            ],
        ]);
    }
}
