<?php

namespace Tests\Tenant;

use Illuminate\Support\Facades\Queue;
use Mtc\ContentManager\Facades\Media as MediaFacade;
use Mtc\MercuryDataModels\Media;
use Mtc\MercuryDataModels\MediaUse;
use Mtc\MercuryDataModels\Vehicle;
use Tests\Support\InteractsWithAutoTraderMedia;
use Tests\TenantTestCase;

class MediaPrimaryUseTest extends TenantTestCase
{
    use InteractsWithAutoTraderMedia;

    /**
     * The initial AutoTrader stock sync must flag the first image of the AT
     * image list as the primary and store the AT list positions as the order.
     */
    public function testInitialAutoTraderSyncSetsFirstImageAsPrimary()
    {
        Queue::fake();

        $vehicle = Vehicle::factory()->create();
        $first = $this->autoTraderMedia('AT-1');
        $second = $this->autoTraderMedia('AT-2');
        $third = $this->autoTraderMedia('AT-3');

        $this->runAutoTraderImageSync($vehicle, [$first, $second, $third]);

        $this->assertSame(0, $this->useFor($vehicle, $first->id)->order);
        $this->assertSame(1, $this->useFor($vehicle, $second->id)->order);
        $this->assertSame(2, $this->useFor($vehicle, $third->id)->order);

        $primaryUses = $this->primaryUses($vehicle);
        $this->assertCount(1, $primaryUses);
        $this->assertSame($first->id, $primaryUses->first()->media_id, 'The first AT image is the primary.');
    }

    /**
     * When AutoTrader reorders the images and the stock is re-synced, the new
     * first image must become the primary. The re-sync batch contains no new
     * media, so nothing reaches setUsesForModel and the primary flag is never
     * moved - the order columns follow AT but the primary stays on the old
     * first image.
     */
    public function testAutoTraderReorderMovesPrimaryToTheNewFirstImage()
    {
        Queue::fake();

        $vehicle = Vehicle::factory()->create();
        $first = $this->autoTraderMedia('AT-1');
        $second = $this->autoTraderMedia('AT-2');
        $third = $this->autoTraderMedia('AT-3');

        $this->runAutoTraderImageSync($vehicle, [$first, $second, $third]);
        $this->assertSame($first->id, $this->primaryUses($vehicle)->first()->media_id);

        // AutoTrader moves the second image into first position.
        $this->runAutoTraderImageSync($vehicle, [$second, $first, $third]);

        $this->assertSame(0, $this->useFor($vehicle, $second->id)->order, 'Order follows the new AT order.');
        $this->assertSame(1, $this->useFor($vehicle, $first->id)->order);

        $primaryUses = $this->primaryUses($vehicle);
        $this->assertCount(1, $primaryUses);
        $this->assertSame(
            $second->id,
            $primaryUses->first()->media_id,
            'The new first AT image must become the primary after a reorder.'
        );
    }

    /**
     * A re-sync that adds a new photo further down the AT list must not steal
     * the primary from the first image. The new photo is the only entry in the
     * assignUses batch, so it is flagged primary (batch index 0) and the real
     * first image has its primary cleared.
     */
    public function testNewImageAddedMidListDoesNotStealPrimary()
    {
        Queue::fake();

        $vehicle = Vehicle::factory()->create();
        $first = $this->autoTraderMedia('AT-1');
        $second = $this->autoTraderMedia('AT-2');
        $third = $this->autoTraderMedia('AT-3');

        $this->runAutoTraderImageSync($vehicle, [$first, $second]);
        $this->assertSame($first->id, $this->primaryUses($vehicle)->first()->media_id);

        // AutoTrader adds a new photo at the end of the list.
        $this->runAutoTraderImageSync($vehicle, [$first, $second, $third]);

        $this->assertSame(2, $this->useFor($vehicle, $third->id)->order);

        $primaryUses = $this->primaryUses($vehicle);
        $this->assertCount(1, $primaryUses);
        $this->assertSame(
            $first->id,
            $primaryUses->first()->media_id,
            'The first AT image must stay primary when a later image is added.'
        );
    }

    /**
     * On a vehicle mixing manually uploaded media with AutoTrader media, the
     * lowest-ordered use wins primary regardless of provider: a manual image at
     * order 0 is the first one displayed, so an AT sync must not steal the
     * primary from it (ties on order are broken by use id, oldest first).
     */
    public function testManualImageAtOrderZeroKeepsPrimaryThroughAnAutoTraderSync()
    {
        Queue::fake();

        $vehicle = Vehicle::factory()->create();
        $manual = Media::factory()->create(['external' => true, 'src' => 'manual-upload.jpg']);
        MediaFacade::setUsesForModel([$manual->id], $vehicle, ['primary' => true], false);

        $first = $this->autoTraderMedia('AT-1');
        $second = $this->autoTraderMedia('AT-2');

        $this->runAutoTraderImageSync($vehicle, [$first, $second]);

        $this->assertSame(0, $this->useFor($vehicle, $manual->id)->order);

        $primaryUses = $this->primaryUses($vehicle);
        $this->assertCount(1, $primaryUses);
        $this->assertSame(
            $manual->id,
            $primaryUses->first()->media_id,
            'The manual image at order 0 displays first, so it stays primary.'
        );
    }

    /**
     * The order-driven primary is AutoTrader-only: other image providers keep
     * the existing behaviour, where a re-sync that adds new media flags the
     * first of the newly added batch as the primary.
     */
    public function testNonAutoTraderProvidersKeepTheBatchPrimaryBehaviour()
    {
        Queue::fake();

        $vehicle = Vehicle::factory()->create();
        $first = $this->providerMedia('phyron', 'P-1');
        $second = $this->providerMedia('phyron', 'P-2');
        $third = $this->providerMedia('phyron', 'P-3');

        $this->runProviderImageSync($vehicle, 'phyron', [$first, $second]);
        $this->assertSame($first->id, $this->primaryUses($vehicle)->first()->media_id);

        // A later sync adds a third image at the end of the list.
        $this->runProviderImageSync($vehicle, 'phyron', [$first, $second, $third]);

        $this->assertSame(2, $this->useFor($vehicle, $third->id)->order, 'Order still follows the list.');

        $primaryUses = $this->primaryUses($vehicle);
        $this->assertCount(1, $primaryUses);
        $this->assertSame(
            $third->id,
            $primaryUses->first()->media_id,
            'Non-AT providers keep the existing behaviour: the newly added batch elects the primary.'
        );
    }

    /**
     * Mirrors the AutoTrader scenario: a vehicle is first synced with a single
     * primary image, then a later sync adds another image as the new primary.
     * The previous primary must be cleared so only one primary remains.
     */
    public function testSettingANewPrimaryClearsThePreviousPrimary()
    {
        Queue::fake();

        $vehicle = Vehicle::factory()->create();
        $firstImage = Media::factory()->create(['external' => true]);
        $secondImage = Media::factory()->create(['external' => true]);

        // Initial sync - first image becomes primary.
        MediaFacade::setUsesForModel([$firstImage->id], $vehicle, ['primary' => true], false);

        $this->assertTrue((bool) $this->useFor($vehicle, $firstImage->id)->primary);

        // Later sync - a newly added image is set as the new primary.
        MediaFacade::setUsesForModel([$secondImage->id], $vehicle, ['primary' => true], false);

        $primaryUses = MediaUse::query()
            ->where('owner_type', $vehicle->getMorphClass())
            ->where('owner_id', $vehicle->id)
            ->where('primary', true)
            ->get();

        $this->assertCount(1, $primaryUses, 'A vehicle must only ever have a single primary image.');
        $this->assertSame($secondImage->id, $primaryUses->first()->media_id);
        $this->assertFalse((bool) $this->useFor($vehicle, $firstImage->id)->primary);
    }

    /**
     * A re-sync (or manual media deletion) can leave a vehicle with images but no primary use,
     * so primaryMediaUse resolves to null and the front-end shows a placeholder. ensurePrimaryUse
     * must promote the lowest-ordered use in that case.
     */
    public function testEnsurePrimaryUsePromotesLowestOrderWhenNoPrimaryExists()
    {
        Queue::fake();

        $vehicle = Vehicle::factory()->create();
        $first = Media::factory()->create(['external' => true]);
        $second = Media::factory()->create(['external' => true]);

        // Attach both with no primary flagged (the broken zero-primary state).
        MediaFacade::setUsesForModel([$first->id, $second->id], $vehicle, ['primary' => false], false);
        $this->assertCount(0, $this->primaryUses($vehicle));

        MediaFacade::ensurePrimaryUse($vehicle);

        $primaryUses = $this->primaryUses($vehicle);
        $this->assertCount(1, $primaryUses, 'A single primary should be elected.');
        $this->assertSame($first->id, $primaryUses->first()->media_id, 'The lowest-ordered use wins.');
    }

    /**
     * ensurePrimaryUse must never override an existing primary (e.g. one chosen manually in admin),
     * only fill in a missing one.
     */
    public function testEnsurePrimaryUseLeavesAnExistingPrimaryUntouched()
    {
        Queue::fake();

        $vehicle = Vehicle::factory()->create();
        $first = Media::factory()->create(['external' => true]);
        $second = Media::factory()->create(['external' => true]);

        MediaFacade::setUsesForModel([$first->id, $second->id], $vehicle, ['primary' => false], false);
        // Mark the higher-ordered image as primary, as a manual selection would.
        $this->useFor($vehicle, $second->id)->update(['primary' => true]);

        MediaFacade::ensurePrimaryUse($vehicle);

        $primaryUses = $this->primaryUses($vehicle);
        $this->assertCount(1, $primaryUses);
        $this->assertSame($second->id, $primaryUses->first()->media_id, 'The existing primary is preserved.');
        $this->assertFalse((bool) $this->useFor($vehicle, $first->id)->primary);
    }

    /**
     * A vehicle with no media at all must be handled without error.
     */
    public function testEnsurePrimaryUseIsANoOpWhenThereIsNoMedia()
    {
        $vehicle = Vehicle::factory()->create();

        MediaFacade::ensurePrimaryUse($vehicle);

        $this->assertCount(0, $this->primaryUses($vehicle));
    }

}
