<?php

namespace Tests\Tenant;

use App\Events\LoadDealershipDataFields;
use App\Services\DealershipBulkUpdateService;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Queue;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Media;
use Mtc\MercuryDataModels\VehicleAttribute;
use Mtc\MercuryDataModels\VehicleAttributeValue;
use Tests\TenantTestCase;

class DealershipBulkUpdateServiceTest extends TenantTestCase
{
    protected bool $mockCountryFacade = true;

    public function testGetFieldsIncludesModelAttributeMediaAndDataFields(): void
    {
        Event::listen(
            LoadDealershipDataFields::class,
            fn () => ['test-integration-field' => ['type' => 'text', 'label' => 'Test Integration Field']]
        );

        $attribute = VehicleAttribute::query()->create([
            'name' => 'Awards',
            'slug' => 'awards',
            'type' => 'text',
            'model' => 'dealership',
            'count' => 1,
            'validation' => [],
            'data' => [],
        ]);

        $fields = collect(new DealershipBulkUpdateService()->getFields());
        $names = $fields->pluck('name');

        $this->assertContains('name', $names, 'Model scalar fields should be present.');
        $this->assertContains('country', $names, 'Country select field should be present.');
        $this->assertContains('media', $names, 'The synthetic image field should be present.');
        $this->assertContains('data::test-integration-field', $names, 'Integration data fields should be present.');
        $this->assertContains("attribute::{$attribute->id}", $names, 'Dealership attributes should be present.');

        $this->assertNotContains('open_times', $names, 'Complex JSON columns must be excluded.');
        $this->assertNotContains('coordinates', $names, 'Composite columns must be excluded.');

        $this->assertSame('boolean', $fields->firstWhere('name', 'active')['type']);
        $this->assertSame('select', $fields->firstWhere('name', 'country')['type']);
        $this->assertSame('image', $fields->firstWhere('name', 'media')['type']);
    }

    public function testSameModeUpdatesScalarColumnAcrossDealerships(): void
    {
        $one = Dealership::factory()->create(['city' => 'Old Town']);
        $two = Dealership::factory()->create(['city' => 'Old Town']);

        $updated = new DealershipBulkUpdateService()->update(
            'same',
            [$one->id, $two->id],
            ['field' => 'city', 'value' => 'Manchester']
        );

        $this->assertSame(2, $updated);
        $this->assertSame('Manchester', $one->fresh()->city);
        $this->assertSame('Manchester', $two->fresh()->city);
    }

    public function testSameModeUpdatesCustomAttribute(): void
    {
        $attribute = VehicleAttribute::query()->create([
            'name' => 'Awards',
            'slug' => 'awards',
            'type' => 'text',
            'model' => 'dealership',
            'count' => 1,
            'validation' => [],
            'data' => [],
        ]);

        $one = Dealership::factory()->create();
        $two = Dealership::factory()->create();

        new DealershipBulkUpdateService()->update(
            'same',
            [$one->id, $two->id],
            ['field' => "attribute::{$attribute->id}", 'value' => 'Dealer of the Year']
        );

        foreach ([$one, $two] as $dealership) {
            $value = VehicleAttributeValue::query()
                ->where('attribute_id', $attribute->id)
                ->where('owner_id', $dealership->id)
                ->where('owner_type', $dealership->getMorphClass())
                ->first();

            $this->assertNotNull($value);
            $this->assertSame('Dealer of the Year', $value->getValue());
        }
    }

    public function testDifferentModeAppliesPerDealershipValues(): void
    {
        $one = Dealership::factory()->create(['city' => 'Old Town']);
        $two = Dealership::factory()->create(['city' => 'Old Town']);

        $updated = new DealershipBulkUpdateService()->update(
            'different',
            [$one->id, $two->id],
            ['updates' => [
                $one->id => ['city' => 'Leeds'],
                $two->id => ['city' => 'Bristol'],
            ]]
        );

        $this->assertSame(2, $updated);
        $this->assertSame('Leeds', $one->fresh()->city);
        $this->assertSame('Bristol', $two->fresh()->city);
    }

    public function testDataFieldWritesIntoJsonColumn(): void
    {
        Event::listen(
            LoadDealershipDataFields::class,
            fn () => ['motors-co-uk-dealer-id' => ['type' => 'text', 'label' => 'Motors.co.uk Dealer ID']]
        );

        $dealership = Dealership::factory()->create(['data' => ['existing' => 'keep-me']]);

        new DealershipBulkUpdateService()->update(
            'same',
            [$dealership->id],
            ['field' => 'data::motors-co-uk-dealer-id', 'value' => 'ABC123']
        );

        $data = $dealership->fresh()->data;
        $this->assertSame('ABC123', $data['motors-co-uk-dealer-id']);
        $this->assertSame('keep-me', $data['existing'], 'Existing data keys must be preserved.');
    }

    public function testMediaFieldWiresMediaUses(): void
    {
        Queue::fake();

        $dealership = Dealership::factory()->create();
        $image = Media::factory()->create(['external' => true]);

        new DealershipBulkUpdateService()->update(
            'same',
            [$dealership->id],
            ['field' => 'media', 'value' => [['id' => $image->id]]]
        );

        $this->assertTrue(
            $dealership->mediaUses()->where('media_id', $image->id)->exists(),
            'The selected media should be attached to the dealership.'
        );
    }

    public function testDisallowedFieldThrows(): void
    {
        $dealership = Dealership::factory()->create();

        $this->expectException(\InvalidArgumentException::class);

        new DealershipBulkUpdateService()->update(
            'same',
            [$dealership->id],
            ['field' => 'open_times', 'value' => 'nope']
        );
    }

    public function testArchiveSoftDeletesSelectedDealerships(): void
    {
        $one = Dealership::factory()->create();
        $two = Dealership::factory()->create();

        $archived = new DealershipBulkUpdateService()->archive([$one->id, $two->id]);

        $this->assertSame(2, $archived);
        $this->assertTrue($one->fresh()->trashed());
        $this->assertTrue($two->fresh()->trashed());

        $this->assertSame(
            0,
            Dealership::query()->whereIn('id', [$one->id, $two->id])->count(),
            'Archived dealerships must drop out of the default query scope.'
        );
        $this->assertSame(
            2,
            Dealership::withTrashed()->whereIn('id', [$one->id, $two->id])->count(),
            'Archived dealerships must still be reachable via withTrashed().'
        );
    }

    public function testArchiveSkipsAlreadyArchivedDealerships(): void
    {
        $active = Dealership::factory()->create();
        $alreadyArchived = Dealership::factory()->create();
        $alreadyArchived->delete();

        $archived = new DealershipBulkUpdateService()->archive([$active->id, $alreadyArchived->id]);

        $this->assertSame(1, $archived, 'Already-archived dealerships must not be counted again.');
        $this->assertTrue($active->fresh()->trashed());
    }
}
