<?php

namespace Tests\Tenant;

use App\Facades\Feature;
use App\Facades\Settings;
use App\Modules\PlaceholderImages\ImaginStudio;
use Carbon\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
use Mtc\ContentManager\Models\MediaSize;
use Mtc\Crm\Models\Form;
use Mtc\Crm\Models\FormQuestion;
use Mtc\Crm\Models\FormSection;
use Mtc\MercuryDataModels\FuelType;
use Mtc\MercuryDataModels\Media;
use Mtc\MercuryDataModels\MediaUse;
use Mtc\MercuryDataModels\NewCar;
use Mtc\MercuryDataModels\Page;
use Mtc\MercuryDataModels\SeoDefault;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\VehicleModel;
use Mtc\MercuryDataModels\VehicleOffer;
use Mtc\MercuryDataModels\VehicleTrim;
use Tests\TenantTestCase;

class NewCarControllerTest extends TenantTestCase
{

    public function testSearch()
    {
        Page::factory()->create(['slug' => 'offers']);
        $make = VehicleMake::factory()->create();

        NewCar::factory(5)
            ->create([
                'make_id' => $make->id,
                'published_at' => Carbon::now()->subDays(3),
            ]);
        NewCar::factory(3);
        $response = $this
            ->postJson(route('new-cars.search', $make->slug));

        $response->assertStatus(200);
        $this->assertArrayHasKey('make', $response->json());
        $this->assertEquals($make->name, $response->json('make'));
        $this->assertArrayHasKey('models', $response->json());
        $this->assertArrayHasKey('offers', $response->json());
    }

    public function testShowCar()
    {
        Http::fake([
            'https://cdn.imagin.studio/getPaints*' => Http::response(['paintData' => ['paintCombinations' => []]])
        ]);
        Feature::setEnabled(tenant(), ['imagin-studio-placeholders']);
        $offer = NewCar::factory()->create(['published' => true]);
        $response = $this
            ->getJson(route('new-cars.show', $offer));
        $response->assertStatus(200);

        $offer2 = NewCar::factory()->create(['published' => false]);
        $response = $this
            ->getJson(route('new-cars.show', $offer2));
        $response->assertStatus(403);

        $response = $this
            ->getJson(route('new-cars.show', [$offer2, 'a' => base64_encode($offer2->id . '-' . $offer2->slug)]));
        $response->assertStatus(200);
    }

    public function testShowCarWithSeoData()
    {
        $car = NewCar::factory()->create([
            'published' => true,
            'seo' => ['title' => 'foo', 'description' => 'baz bar']
        ]);
        $response = $this
            ->getJson(route('new-cars.show', $car));

        $response->assertStatus(200);
        $this->assertEquals('foo', $response->json('seo.title'));
        $this->assertEquals('baz bar', $response->json('seo.description'));

        SeoDefault::query()->create([
            'section' => 'new-car',
            'title' => '{{TITLE}} | {{MAKE}} Offers | {{SITE_NAME}} ',
            'description' => '{{CONTENT_EXCERPT}}',
        ]);

        $make = VehicleMake::factory()->create();
        $model = VehicleModel::factory()->create();
        $car = NewCar::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model->id,
            'published' => true,
        ]);
        $response = $this
            ->getJson(route('new-cars.show', $car));

        $response->assertStatus(200);
        $this->assertStringContainsString($car->name, $response->json('seo.title'));
        $this->assertStringContainsString($make->name, $response->json('seo.title'));
    }

    public function testCarHasForm()
    {
        $form = Form::factory()->create();
        $section = FormSection::factory()->create(['form_id' => $form->id]);
        FormQuestion::factory()->create([
            'form_id' => $form->id,
            'form_section_id' => $section->id,
            'type' => 'text',
            'name' => 'First Name',
            'validation' => 'required',
        ]);

        $car = NewCar::factory()->create(['published' => true]);

        $response = $this
            ->getJson(route('new-cars.show', $car));

        $response->assertStatus(200);

        $car->update([
            'form_id' => $form->id,
        ]);
        $response = $this
            ->getJson(route('new-cars.show', $car));

        $response->assertStatus(200);
        $this->assertIsArray($response->json('forms.enquire'));
        $this->assertEquals($form->id, $response->json('forms.enquire.id'));
    }

    public function testTrims()
    {
        $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,
            'published' => true,
        ]);
        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',
        ]);

        $response = $this
            ->getJson(route('new-cars.show', $offer));

        $response->assertStatus(200);
        $this->assertArrayHasKey('trims', $response->json());
        $this->assertCount(3, VehicleTrim::all());
        $this->assertCount(2, $response->json('trims'));
    }

    public function testTrimMedia()
    {
        $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,
            'published' => true,
        ]);

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

        MediaSize::factory(3)->create([
            'model' => 'new-car',
        ]);

        $media_item = Media::factory()->create([
            'src' => 'foo.jpg',
            'type' => 'image',
        ]);

        $media_item2 = Media::factory()->create([
            'src' => 'baz.jpg',
            'type' => 'image',
        ]);

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

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

        $response = $this
            ->getJson(route('new-cars.show', $offer));

        $response->assertStatus(200);

        $this->assertArrayHasKey('trims', $response->json());
        $this->assertCount(1, $response->json('trims'));
        $this->assertArrayHasKey('media_uses', $response->json('trims.0'));
        $this->assertArrayHasKey('image', $response->json('trims.0'));
        $this->assertArrayHasKey('sizes', $response->json('trims.0.image'));
        $this->assertCount(3, $response->json('trims.0.image.sizes'));
        $this->assertArrayHasKey('listing_image', $response->json('trims.0'));
        $this->assertArrayHasKey('sizes', $response->json('trims.0.listing_image'));
        $this->assertCount(6, $response->json('trims.0.listing_image.sizes'));

        foreach ($response->json('trims.0.image.sizes') as $size_name => $size_path) {
            $this->assertStringContainsString($media_item->src, $size_path);
        }

        foreach ($response->json('trims.0.listing_image.sizes') as $size_name => $size_path) {
            $this->assertStringContainsString($media_item2->src, $size_path);
        }
    }

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

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

        $response = $this
            ->getJson(route('new-cars.show', $offer));

        $response->assertStatus(200);
        $this->assertArrayHasKey('imagin_studio_base_url', $response->json());
        $this->assertNull($response->json('imagin_studio_base_url'));
    }

    public function testImaginStudioUrl()
    {
        Feature::setEnabled(tenant(), ['imagin-studio-placeholders']);

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

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

        $response = $this
            ->getJson(route('new-cars.show', $offer));

        $response->assertStatus(200);
        $this->assertArrayHasKey('imagin_studio_base_url', $response->json());
        $this->assertStringContainsString('cdn.imagin.studio/getImage?customer=', $response->json('imagin_studio_base_url'));
    }

    public function testFuelType()
    {
        Page::factory()->create(['slug' => 'offers']);
        $make = VehicleMake::factory()->create();
        $model = VehicleModel::factory()->create([
            'make_id' => $make->id
        ]);

        $fuel_type_foo = FuelType::factory()->create([
            'name' => 'foo'
        ]);

        $newCars = NewCar::factory(5)
            ->create([
                'make_id' => $make->id,
                'model_id' => $model->id,
                'published' => true,
                'published_at' => Carbon::now()->subDays(3),
            ]);

        $newCars->each(function($car) use ($fuel_type_foo) {
            $car->fuelTypes()->attach($fuel_type_foo->id);
        });

        $response = $this
            ->postJson(route('new-cars.search', $make->slug));
        $response->assertStatus(200);
        $this->assertCount(5, NewCar::all());
        $this->assertArrayHasKey('offers', $response->json());
        $this->assertArrayHasKey('fuel_type', $response->json('offers.data.0'));
        $this->assertEquals($fuel_type_foo->name, $response->json('offers.data.0.fuel_type'));
    }

    public function testImaginStudioClientKey()
    {
        Config::set('services.imagin-studio.key', 'test-key');

        Settings::make([
            'tab' => 'foo',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'imagin-placeholders-client-key',
            'value' => 'abc123',
            'type' => 'string',
        ]);

        $this->assertEquals('test-key', config('services.imagin-studio.key'));

        $new_car = NewCar::factory()->create();
        $vehicle = Vehicle::factory()->create();
        $vehicle_offer = VehicleOffer::factory()->create();

        $url = new ImaginStudio()->getForNewCar($new_car);
        $this->assertStringContainsString('customer=abc123', $url);
        $this->assertStringNotContainsString('billingTag', $url);

        $url = new ImaginStudio()->getForVehicle($vehicle);
        $this->assertStringContainsString('customer=abc123', $url);
        $this->assertStringNotContainsString('billingTag', $url);

        $url = new ImaginStudio()->getForOffer($vehicle_offer);
        $this->assertStringContainsString('customer=abc123', $url);
        $this->assertStringNotContainsString('billingTag', $url);

        $url = new ImaginStudio()->getForOther('foo', 'baz', 'bar');
        $this->assertStringContainsString('customer=abc123', $url);
        $this->assertStringNotContainsString('billingTag', $url);

        $url = new ImaginStudio()->getBaseUrl();
        $this->assertStringContainsString('customer=abc123', $url);
        $this->assertStringNotContainsString('billingTag', $url);
    }

    public function testImaginStudioClientKeyFallback()
    {
        Config::set('services.imagin-studio.key', 'test-key');

        $this->assertEquals('test-key', config('services.imagin-studio.key'));

        $vehicle = Vehicle::factory()->create();
        $url = new ImaginStudio()->getForVehicle($vehicle);
        $this->assertStringContainsString('customer=test-key', $url);
        $this->assertStringNotContainsString('billingTag', $url);
    }

    public function testSearchReturnsImaginStudiBaseUrl()
    {
        Feature::setEnabled(tenant(), ['imagin-studio-placeholders']);

        Page::factory()->create(['slug' => 'offers']);
        $make = VehicleMake::factory()->create();

        NewCar::factory(5)
            ->create([
                'make_id' => $make->id,
                'published_at' => Carbon::now()->subDays(3),
            ]);

        $response = $this
            ->postJson(route('new-cars.search', $make->slug));

        $response->assertStatus(200);

        $this->assertArrayHasKey('imagin_studio_base_url', $response->json());
        $this->assertNotEmpty($response->json('imagin_studio_base_url'));
    }

    public function testFuelTypes()
    {
        Page::factory()->create(['slug' => 'offers']);

        $make = VehicleMake::factory()->create();

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

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


        $fuel_type_foo = FuelType::factory()->create([
            'name' => 'foo'
        ]);

        $newCars = NewCar::factory()
            ->create([
                'make_id' => $make->id,
                'model_id' => $model_1->id,
                'published' => true,
                'published_at' => Carbon::now()->subDays(3),
            ]);

        $newCars->each(function($car) use ($fuel_type_foo) {
            $car->fuelTypes()->attach($fuel_type_foo->id);
        });

        $newCars = NewCar::factory()
            ->create([
                'make_id' => $make->id,
                'model_id' => $model_2->id,
                'published' => true,
                'published_at' => Carbon::now()->subDays(3),
            ]);

        $newCars->each(function($car) use ($fuel_type_foo) {
            $car->fuelTypes()->attach($fuel_type_foo->id);
        });

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

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

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

        VehicleOffer::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model_2->id,
            'trim' => 'foo_too',
        ]);

        $response = $this
            ->postJson(route('new-cars.search', $make->slug));
        $response->assertStatus(200);
        $this->assertCount(2, NewCar::all());
        $this->assertArrayHasKey('offers', $response->json());
        $this->assertArrayHasKey('fuel_types', $response->json('offers.data.0'));
        $this->assertArrayHasKey('bar', $response->json('offers.data.0.fuel_types'));
        $this->assertArrayHasKey('bar', $response->json('offers.data.1.fuel_types'));

        // check that items contain the expected fuel types
        if (array_key_exists('buzz', $response->json('offers.data.0.fuel_types'))) {
            $this->assertArrayHasKey('baz', $response->json('offers.data.1.fuel_types'));
        } else {
            $this->assertArrayHasKey('baz', $response->json('offers.data.0.fuel_types'));
        }
    }

    public function testFromPrice()
    {
        Page::factory()->create(['slug' => 'offers']);
        $make = VehicleMake::factory()->create();
        $model_1 = VehicleModel::factory()->create([
            'make_id' => $make->id
        ]);
        $model_2 = VehicleModel::factory()->create([
            'make_id' => $make->id
        ]);
        $fuel_type_foo = FuelType::factory()->create([
            'name' => 'foo'
        ]);
        $newCars = NewCar::factory()
            ->create([
                'make_id' => $make->id,
                'model_id' => $model_1->id,
                'published' => true,
                'published_at' => Carbon::now()->subDays(3),
            ]);
        $newCars->each(function($car) use ($fuel_type_foo) {
            $car->fuelTypes()->attach($fuel_type_foo->id);
        });
        $newCars = NewCar::factory()
            ->create([
                'make_id' => $make->id,
                'model_id' => $model_2->id,
                'published' => true,
                'published_at' => Carbon::now()->subDays(3),
            ]);
        $newCars->each(function($car) use ($fuel_type_foo) {
            $car->fuelTypes()->attach($fuel_type_foo->id);
        });
        VehicleOffer::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model_2->id,
            'price' => 1,
        ]);

        VehicleOffer::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model_1->id,
            'price' => 789,
        ]);

        VehicleOffer::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model_1->id,
            'price' => 123,
        ]);

        VehicleOffer::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model_2->id,
            'price' => 456,
        ]);

        $response = $this
            ->postJson(route('new-cars.search', $make->slug));
        $response->assertStatus(200);
        $this->assertCount(2, NewCar::all());
        $this->assertArrayHasKey('offers', $response->json());
        $this->assertArrayHasKey('from_price', $response->json('offers.data.0'));
        $this->assertEquals(123, $response->json('offers.data.0.from_price'));
    }

    public function testEmptyFromPrice()
    {
        Page::factory()->create(['slug' => 'offers']);
        $make = VehicleMake::factory()->create();
        $model_1 = VehicleModel::factory()->create([
            'make_id' => $make->id
        ]);

        $fuel_type_foo = FuelType::factory()->create([
            'name' => 'foo'
        ]);

        $newCars = NewCar::factory()
            ->create([
                'make_id' => $make->id,
                'model_id' => $model_1->id,
                'published' => true,
                'published_at' => Carbon::now()->subDays(3),
            ]);

        $newCars->each(function($car) use ($fuel_type_foo) {
            $car->fuelTypes()->attach($fuel_type_foo->id);
        });

        $newCars->each(function($car) use ($fuel_type_foo) {
            $car->fuelTypes()->attach($fuel_type_foo->id);
        });

        VehicleOffer::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model_1->id,
            'price' => 0,
        ]);

        VehicleOffer::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model_1->id,
            'price' => 123,
        ]);

        $response = $this
            ->postJson(route('new-cars.search', $make->slug));

        $response->assertStatus(200);
        $this->assertCount(1, NewCar::all());
        $this->assertArrayHasKey('offers', $response->json());
        $this->assertArrayHasKey('from_price', $response->json('offers.data.0'));
        $this->assertEquals(123, $response->json('offers.data.0.from_price'));
    }

    public function testVehicleType()
    {
        $make = VehicleMake::factory()->create();
        $model = VehicleModel::factory()->create();

        NewCar::factory(2)->create([
            'published' => true,
            'published_at' => Carbon::now()->subDays(3),
            'make_id' => $make->id,
            'model_id' => $model->id,
            'vehicle_type' => 'car',
        ]);

        NewCar::factory(3)->create([
            'published' => true,
            'published_at' => Carbon::now()->subDays(3),
            'make_id' => $make->id,
            'model_id' => $model->id,
            'vehicle_type' => 'lcv',
        ]);

        $response = $this
            ->postJson(route('new-cars.search', $make->slug), [
                'vehicle_type' => 'car'
            ]);

        $response->assertStatus(200);
        $this->assertArrayHasKey('offers', $response->json());
        $this->assertCount(2, $response->json('offers.data'));

        $response = $this
            ->postJson(route('new-cars.search', $make->slug), [
                'vehicle_type' => 'lcv'
            ]);

        $response->assertStatus(200);
        $this->assertArrayHasKey('offers', $response->json());
        $this->assertCount(3, $response->json('offers.data'));

        $response = $this
            ->postJson(route('new-cars.search', $make->slug));

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