<?php

namespace Tests\Feature\Controllers;

use App\Facades\Settings;
use Database\Seeders\Global\NewVehiclePermissionSeed;
use Database\Seeders\MercuryTheme\ContentElementSeeder;
use Database\Seeders\MercuryTheme\TemplateSeeder;
use Mtc\MercuryDataModels\Form;
use Mtc\MercuryDataModels\Franchise;
use Illuminate\Support\Facades\Bus;
use Mtc\ContentManager\Jobs\GenerateSizeForMediaFileJob;
use Mtc\MercuryDataModels\Media;
use Mtc\MercuryDataModels\MediaUse;
use Mtc\MercuryDataModels\NewCar;
use Mtc\MercuryDataModels\NewCarContentHistory;
use Mtc\MercuryDataModels\Template;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\VehicleModel;
use Mtc\MercuryDataModels\VehicleTrim;
use Tests\DatabaseTestCase;
use Tests\UserForTenant;

class NewCarControllerTest extends DatabaseTestCase
{
    use UserForTenant;


    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function testIndex()
    {
        NewCar::factory(5)->create();
        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.new-cars.index'));

        $response->assertStatus(200);
        $this->assertIsArray($response->json('data'));
        $this->assertCount(5, $response->json('data'));
    }

    public function testStore()
    {
        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.new-cars.store'), [
                'name' => 'foo',
            ]);

        $response->assertStatus(201);
        $this->assertEquals('foo', $response->json('newVehicle.name'));
        $this->assertTrue(NewCar::query()->where('name', 'foo')->exists());

    }

    public function testBulkCreateForFranchise()
    {
        $this->seed(NewVehiclePermissionSeed::class);
        $user = $this->getUser('Administrator');
        $user->givePermissionTo('edit-new-vehicles');
        $user->load('permissions');

        $make = VehicleMake::factory()->create(['name' => 'Ford']);
        $franchise = Franchise::factory()->create(['make_id' => $make->id]);
        $modelA = VehicleModel::factory()->create(['make_id' => $make->id, 'name' => 'Fiesta', 'type' => 'car']);
        $modelB = VehicleModel::factory()->create(['make_id' => $make->id, 'name' => 'Focus', 'type' => 'car']);
        $otherMakeModel = VehicleModel::factory()->create(['name' => 'Golf']);

        $template = Template::query()->forceCreate(['name' => 'Bulk Template', 'status' => 'active']);
        $enquiryForm = Form::query()->forceCreate(['name' => 'Bulk Enquiry Form', 'is_active' => 1]);
        $testDriveForm = Form::query()->forceCreate(['name' => 'Bulk Test Drive Form', 'is_active' => 1]);

        $response = $this->actingAs($user)
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.new-cars.bulk-create'), [
                'franchise_id' => $franchise->id,
                'model_ids' => [$modelA->id, $modelB->id, $otherMakeModel->id],
                'template_id' => $template->id,
                'form_id' => $enquiryForm->id,
                'test_drive_form_id' => $testDriveForm->id,
                'vehicle_type' => 'lcv',
            ]);

        $response->assertStatus(201);
        // The model from a different make is skipped - only the franchise's make models are created.
        $this->assertEquals(2, $response->json('created_count'));
        $this->assertCount(2, $response->json('data'));
        $this->assertEquals(2, NewCar::query()->where('franchise_id', $franchise->id)->count());
        $this->assertTrue(
            NewCar::query()
                ->where('model_id', $modelA->id)
                ->where('make_id', $make->id)
                ->where('name', 'Ford Fiesta')
                ->where('template_id', $template->id)
                ->where('form_id', $enquiryForm->id)
                ->where('test_drive_form_id', $testDriveForm->id)
                ->where('vehicle_type', 'lcv')
                ->exists()
        );
        $this->assertFalse(NewCar::query()->where('model_id', $otherMakeModel->id)->exists());
    }

    public function testBulkCreateForFranchiseRequiresPermission()
    {
        $franchise = Franchise::factory()->create();
        $model = VehicleModel::factory()->create(['make_id' => $franchise->make_id]);

        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.new-cars.bulk-create'), [
                'franchise_id' => $franchise->id,
                'model_ids' => [$model->id],
            ]);

        $response->assertStatus(403);
    }

    public function testShow()
    {
        $offer = NewCar::factory()->create([
            'name' => 'foo',
        ]);

        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.new-cars.show', $offer));

        $response->assertStatus(200);
        $this->assertEquals('foo', $response->json('newVehicle.name'));
    }

    public function testCopy()
    {
        $newCar = NewCar::factory()->create([
            'name' => 'foo',
        ]);

        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.new-cars.copy', $newCar));

        $response->assertStatus(201);
        $this->assertTrue(NewCar::query()->where('id', $response->json('newVehicle.id'))->exists());
        $this->assertNotEquals($newCar->id, $response->json('newVehicle.id'));
        $this->assertEquals($newCar->name, $response->json('newVehicle.name'));
    }

    public function testUpdate()
    {
        $offer = NewCar::factory()->create([
            'name' => 'foo',
        ]);

        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.new-cars.update', $offer), [
                'name' => 'baz',
                'featured' => 0,
                'published' => 1,
                'derivative' => 'foo baz bar',
                'make_id' => 8,
                'model_id' => 9,
                'type_id' => 10,
                'form_id' => 11,
                'seo' => [],
                'data' => [],
                'content' => [],
            ]);

        $offer->refresh();
        $response->assertStatus(200);
        $this->assertEquals('baz', $response->json('newVehicle.name'));
        $this->assertEquals(10, $response->json('newVehicle.type_id'));
        $this->assertEquals('baz', $offer->name);
        $this->assertEquals('foo baz bar', $offer->derivative);
        $this->assertEquals(8, $offer->make_id);
        $this->assertEquals(9, $offer->model_id);
        $this->assertEquals(10, $offer->type_id);
        $this->assertEquals(11, $offer->form_id);
        $this->assertEquals(1, $offer->published);
    }

    public function testDestroy()
    {
        $offer = NewCar::factory()->create();

        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->deleteJson(route('tenant.new-cars.destroy', $offer));

        $response->assertStatus(200);
        $this->assertFalse(NewCar::query()->where('id', $offer->id)->exists());
    }

    public function testTemplateSync()
    {
        $this->seed(ContentElementSeeder::class);
        $this->seed(TemplateSeeder::class);
        $template = Template::query()->where('slug', 'offers-list')->first();
        $offer = NewCar::factory()->create(['template_id' => $template->id]);

        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.new-cars.sync-template', $offer));
        $response->assertStatus(200);
    }

    public function testVersionRestore()
    {
        $offer = NewCar::factory()->create();
        $version = NewCarContentHistory::factory()->create(['car_id' => $offer->id]);

        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.new-cars.restore-version', [$offer, $version]));
        $response->assertStatus(200);
        $version->refresh();
        $this->assertTrue($version->is_active);
    }

    public function testTrimSave()
    {
        $offer = NewCar::factory()->create([
            'name' => 'foo',
            'make_id' => 123,
            'model_id' => 456,
        ]);

        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.new-cars.update', $offer), [
                'name' => 'baz',
                'make_id' => 123,
                'model_id' => 456,
                'trims' => [
                    [
                        'name' => 'foo',
                        'active' => true,
                        'mpg' => 12.34,
                        'emissions' => 56.78,
                        'engine_size' => 1.2,
                        'bhp' => 555,
                        'zero_to_sixty' => 99,
                        'doors' => 4,
                        'seats' => 5,
                        'battery_size' => 321,
                        'range_miles' => 654,
                        'battery_charge_time' => 789,
                        'battery_quick_charge_time' => 987,
                        'wheelbase' => 'lwb',
                        'fuel_types' => [
                            'petrol',
                            'diesel',
                        ],
                        'colours' => [
                            [
                                'name' => 'red',
                                'hex_code' => '#ff0000',
                            ],
                            [
                                'name' => 'green',
                                'hex_code' => '#00ff00',
                            ],
                        ],
                        'transmission_types' => [
                            'automatic',
                            'manual',
                        ],
                    ]
                ],
            ]);

        $response->assertStatus(200);
        $this->assertCount(1, VehicleTrim::all());

        $trim = VehicleTrim::query()->first();
        $this->assertNotEmpty($trim);
        $this->assertEquals('foo', $trim->name);
        $this->assertContains('petrol', $trim->fuel_types);
        $this->assertContains('diesel', $trim->fuel_types);
        $this->assertNotEmpty($trim->colours);

        $available_colours = [];

        collect($trim->colours)->each(function ($colour) use (&$available_colours) {
            $available_colours[$colour['name']] = $colour['hex_code'];
        });

        $this->assertArrayHasKey('red', $available_colours);
        $this->assertArrayHasKey('green', $available_colours);

        $this->assertEquals('#ff0000', $available_colours['red']);
        $this->assertEquals('#00ff00', $available_colours['green']);

        $this->assertEquals(true, $trim->active);
        $this->assertEquals(123, $trim->make_id);
        $this->assertEquals(456, $trim->model_id);
        $this->assertEquals(12.34, $trim->mpg);
        $this->assertEquals(56.78, $trim->emissions);
        $this->assertEquals(1.2, $trim->engine_size);
        $this->assertEquals(555, $trim->bhp);
        $this->assertEquals(99, $trim->zero_to_sixty);
        $this->assertEquals(4, $trim->doors);
        $this->assertEquals(5, $trim->seats);
        $this->assertEquals(321, $trim->battery_size);
        $this->assertEquals(654, $trim->range_miles);
        $this->assertEquals(789, $trim->battery_charge_time);
        $this->assertEquals(987, $trim->battery_quick_charge_time);
        $this->assertEquals('lwb', $trim->wheelbase);
        $this->assertContains('automatic', $trim->transmission_types);
        $this->assertContains('manual', $trim->transmission_types);
    }

    public function testAvailableTrims()
    {
        $make = VehicleMake::factory()->create();
        $model_1 = VehicleModel::factory()->create([
            'make_id' => $make->id,
        ]);
        $model_2 = VehicleModel::factory()->create([
            'make_id' => $make->id,
        ]);

        $offer = NewCar::factory()->create([
            'name' => 'foo',
            'make_id' => $make->id,
            'model_id' => $model_1->id,
        ]);

        $trim = VehicleTrim::query()->create([
            'make_id' => $make->id,
            'model_id' => $model_1->id,
            'name' => 'abc',
        ]);

        VehicleTrim::query()->create([
            'make_id' => $make->id,
            'model_id' => $model_1->id,
            'name' => 'def',
        ]);

        VehicleTrim::query()->create([
            'make_id' => $make->id,
            'model_id' => $model_2->id,
            'name' => 'xyz',
        ]);

        $media_item = Media::factory()->create();
        $media_item2 = Media::factory()->create();

        MediaUse::factory()->create([
            'media_id' => $media_item->id,
            'owner_type' => 'vehicle-trim',
            'owner_id' => $trim->id,
            'primary' => true,
        ]);

        MediaUse::factory()->create([
            'media_id' => $media_item2->id,
            'owner_type' => 'vehicle-trim',
            'owner_id' => $trim->id,
            'primary' => false,
        ]);

        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.new-cars.show', $offer));

        $response->assertStatus(200);
        $this->assertEquals('foo', $response->json('newVehicle.name'));
        $this->assertCount(3, VehicleTrim::all());
        $this->assertCount(2, $response->json('newVehicle.trims'));
        $this->assertArrayHasKey('active', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('model_id', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('fuel_types', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('colours', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('transmission_types', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('mpg', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('emissions', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('engine_size', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('bhp', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('zero_to_sixty', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('doors', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('seats', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('battery_size', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('range_miles', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('battery_charge_time', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('battery_quick_charge_time', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('wheelbase', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('image', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('image_uses', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('listing_image', $response->json('newVehicle.trims.0'));
        $this->assertArrayHasKey('listing_image_uses', $response->json('newVehicle.trims.0'));

        $this->assertEquals($media_item->id, $response->json('newVehicle.trims.0.image.0'));
        $this->assertEquals($media_item2->id, $response->json('newVehicle.trims.0.listing_image.0'));
    }

    public function testDuplicateNameWithTrimCreate()
    {
        $make = VehicleMake::factory()->create();
        $model = VehicleModel::factory()->create([
            'make_id' => $make->id,
        ]);

        $offer = NewCar::factory()->create([
            'name' => 'test',
            'make_id' => $make->id,
            'model_id' => $model->id,
        ]);

        VehicleTrim::query()->create([
            'make_id' => $make->id,
            'model_id' => $model->id,
            'new_car_id' => $offer->id,
            'name' => 'foo',
        ]);


        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.new-cars.update', $offer), [
                'name' => 'foo',
                'make_id' => $make->id,
                'model_id' => $model->id,
                'trims' => [
                    [
                        'name' => 'foo',
                        'active' => true,
                    ]
                ]
            ]);

        $response->assertStatus(422);
    }

    public function testDuplicateNameWithTrimUpdate()
    {
        $make = VehicleMake::factory()->create();
        $model = VehicleModel::factory()->create([
            'make_id' => $make->id,
        ]);


        $offer = NewCar::factory()->create([
            'name' => 'test',
            'make_id' => $make->id,
            'model_id' => $model->id,
        ]);

        $trim_foo = VehicleTrim::query()->create([
            'make_id' => $make->id,
            'model_id' => $model->id,
            'new_car_id' => $offer->id,
            'name' => 'foo',
        ]);

        $trim_bar = VehicleTrim::query()->create([
            'make_id' => $make->id,
            'model_id' => $model->id,
            'new_car_id' => $offer->id,
            'name' => 'bar',
        ]);


        $response = $this->actingAs($this->getUser(false))
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.new-cars.update', $offer), [
                'name' => 'foo',
                'make_id' => $make->id,
                'model_id' => $model->id,
                'trims' => [
                    [
                        'id' => $trim_foo->id,
                        'name' => 'bar',
                        'active' => true,
                    ],
                    [
                        'id' => $trim_bar->id,
                        'name' => 'bar',
                        'active' => true,
                    ],
                ]
            ]);

        $response->assertStatus(422);
    }

    public function testUpdateImages()
    {
        Bus::fake();
        $media = Media::factory()->create();
        $media2 = Media::factory()->create();
        $media3 = Media::factory()->create();
        $offer = NewCar::factory()->create([
            'name' => 'foo',
        ]);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.new-cars.update', $offer), [
                'name' => 'baz',
                'featured' => 0,
                'published' => 1,
                'derivative' => 'foo baz bar',
                'make_id' => 8,
                'model_id' => 9,
                'type_id' => 10,
                'form_id' => 11,
                'seo' => [],
                'data' => [],
                'content' => [],
                'listing_image' => [$media->id],
                'hero_image' => [$media2->id],
                'mobile_hero_image' => [$media3->id],
            ]);

        Bus::assertDispatched(GenerateSizeForMediaFileJob::class);
        $response->assertStatus(200);
        $this->assertEquals([$media->id], $response->json('newVehicle.listing_image'));
        $this->assertEquals([$media2->id], $response->json('newVehicle.hero_image'));
        $this->assertEquals([$media3->id], $response->json('newVehicle.mobile_hero_image'));
    }

    public function testContentUpdateAdVersionSaving()
    {
        tenant()->tier = 'pro';
        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'value' => true,
            'type' => 'bool',
            'config_key' => 'new-cars-versioning-enabled',
        ]);
        $this->seed(ContentElementSeeder::class);
        $this->seed(TemplateSeeder::class);
        $template = Template::query()->where('slug', 'offers-list')->first();
        $offer = NewCar::factory()->create([
            'name' => 'foo',
            'template_id' => $template->id,
        ]);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.new-cars.sync-template', $offer));

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.new-cars.show', $offer));

        $newCar = $response->json('newVehicle');

        $newCar['content'][0]['children'][0]['content'] = 'foo';
        $newCar['content'][0]['children'][1]['content'] = 'baz bar';
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.new-cars.update', $offer), $newCar);

        $response->assertStatus(200);
        $this->assertEquals('foo', $response->json('newVehicle.content.0.children.0.content'));
        $this->assertEquals('baz bar', $response->json('newVehicle.content.0.children.1.content'));
        $this->assertEquals(1, $offer->versions()->count());
    }

    public function testUpdatingTrimMedia()
    {
        Bus::fake();

        $media_item = Media::factory()->create();
        $media_item2 = Media::factory()->create();

        $make = VehicleMake::factory()->create();
        $model_1 = VehicleModel::factory()->create([
            'make_id' => $make->id,
        ]);

        $offer = NewCar::factory()->create([
            'name' => 'foo',
            'make_id' => $make->id,
            'model_id' => $model_1->id,
        ]);

        $trim = VehicleTrim::query()->create([
            'make_id' => $make->id,
            'model_id' => $model_1->id,
            'name' => 'abc',
        ]);

        $this->assertCount(1, $offer->getTrims());

        $this->assertCount(
            0,
            MediaUse::query()
                ->where('owner_type', '=', 'vehicle-trim')
                ->get()
        );

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->withoutExceptionHandling()
            ->putJson(route('tenant.new-cars.update', $offer), [
                'name' => 'baz',
                'featured' => 0,
                'published' => 1,
                'derivative' => 'foo baz bar',
                'make_id' => 8,
                'model_id' => 9,
                'type_id' => 10,
                'form_id' => 11,
                'seo' => [],
                'data' => [],
                'content' => [],
                'trims' => [
                    [
                        'name' => 'foo',
                        'active' => true,
                        'image' => [
                            $media_item->id
                        ],
                        'listing_image' => [
                            $media_item2->id
                        ],
                    ],
                ],
            ]);

        Bus::assertDispatched(GenerateSizeForMediaFileJob::class);
        $response->assertStatus(200);

        $this->assertCount(
            2,
            MediaUse::query()
                ->where('owner_type', '=', 'vehicle-trim')
                ->get()
        );
    }
}
