<?php

namespace Tests\Feature;

use App\Facades\Settings;
use App\Jobs\BulkImportJob;
use App\Models\ImportMap;
use App\VehicleSpec\Jobs\VehicleCapIdLookup;
use Database\Seeders\Global\CountrySeeder;
use Database\Seeders\Tenant\VehicleSpecSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Client\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Queue;
use Mtc\MercuryDataModels\Finance\Jobs\FetchVehicleFinanceData;
use Mtc\MercuryDataModels\Vehicle;
use Tests\TestCase;
use Tests\UserForTenant;

class VehicleControllerTest extends TestCase
{
    use RefreshDatabase;
    use UserForTenant;

    protected $tenancy = true;
    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function testIndex()
    {
        Vehicle::factory(4)->create();
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.vehicles.index'));

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

    public function testStore()
    {
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.store'), [
                'title' => 'foo baz bar',
            ]);

        $response->assertStatus(201);

        $this->assertEquals('foo baz bar', $response->json('data.vehicle.title'));
        $this->assertNotNull($response->json('data.vehicle.uuid'));
        $this->assertTrue(Vehicle::query()->where('title', 'foo baz bar')->exists());
        $this->assertTrue(Vehicle::query()->where('id', $response->json('data.vehicle.id'))->exists());
    }

    public function testShow()
    {
        $vehicle = Vehicle::factory()->create([
            'make_id' => 10,
            'registration_number' => 'AB23DEV',
            'title' => 'foo baz bar',
            'derivative' => 'foo-baz-bar',
        ]);
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.vehicles.show', $vehicle));

        $response->assertStatus(200);

        $this->assertEquals(10, $response->json('data.vehicle.make_id'));
        $this->assertEquals('AB23 DEV', $response->json('data.vehicle.registration_number'));
        $this->assertEquals('foo baz bar', $response->json('data.vehicle.title'));
        $this->assertEquals('foo-baz-bar', $response->json('data.vehicle.derivative'));

    }

    public function testUpdate()
    {
        $this->seed(CountrySeeder::class);
        $vehicle = Vehicle::factory()->create(['slug' => null]);
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.vehicles.update', $vehicle), [
                'title' => 'lorem ipsum',
                'derivative' => 'foo baz bar',
                'registration_number' => 'AB12 DEV',
                'make_id' => 6,
                'is_published' => true,
                'model_id' => 7,
                'dealership_id' => 8,
                'transmission_id' => 9,
                'fuel_type_id' => 10,
                'drivetrain_id' => 11,
                'colour' => 'blue',
                'price' => 8900,
                'monthly_price' => 120,
                'deposit' => 3000,
                'door_count' => 4,
                'seats' => 5,
                'primary_media' => null,
                'manufacture_year' => 2020,
                'odometer_mi' => 100000,
                'engine_size_cc' => 1990,
                'co2' => 112,
                'mpg' => 44.4,
                'previous_owner_count' => 2,
                'first_registration_date' => '2020-01-03',
                'finance_examples' => [
                    [
                        'id' => 'abc-123',
                        'term' => 60,
                        'number_of_payments' => 40,
                        'finance_type' => 'HP',
                        'monthly_price' => 1400,
                        'apr' => 8.9,
                        'deposit' => 7000,
                    ]
                ]
            ]);

        $response->assertStatus(200);

        $vehicle->refresh();
        $this->assertNotNull($vehicle->slug);
        $this->assertEquals('lorem ipsum', $vehicle->title);
        $this->assertEquals('foo baz bar', $vehicle->derivative);
        $this->assertEquals('AB12 DEV', $vehicle->registration_number);
        $this->assertEquals(6, $vehicle->make_id);
        $this->assertEquals(7, $vehicle->model_id);
        $this->assertEquals(8, $vehicle->dealership_id);
        $this->assertEquals(9, $vehicle->transmission_id);
        $this->assertEquals(10, $vehicle->fuel_type_id);
        $this->assertEquals(11, $vehicle->drivetrain_id);
        $this->assertEquals('blue', $vehicle->colour);
        $this->assertEquals(8900, $vehicle->price);
        $this->assertEquals(120, $vehicle->monthly_price);
        $this->assertEquals(3000, $vehicle->deposit);
        $this->assertEquals(4, $vehicle->door_count);
        $this->assertEquals(5, $vehicle->seats);
        $this->assertEquals(2020, $vehicle->manufacture_year);
        $this->assertEquals(100000, $vehicle->odometer_mi);
        $this->assertEquals(1990, $vehicle->engine_size_cc);
        $this->assertEquals(112, $vehicle->co2);
        $this->assertEquals(44.4, $vehicle->mpg);
        $this->assertEquals(2, $vehicle->previous_owner_count);
        $this->assertEquals('2020-01-03', $vehicle->first_registration_date->format('Y-m-d'));
    }

    public function testUpdateDiferentRegNumberFormats()
    {
        $this->seed(CountrySeeder::class);
        $vehicle = Vehicle::factory()->create();
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.vehicles.update', $vehicle), [
                'title' => 'lorem ipsum',
                'derivative' => 'foo baz bar',
                'registration_number' => 'ab12 csw',
                'is_published' => true,
                'primary_media' => null,
                'make_id' => 6,
                'model_id' => 7,
                'dealership_id' => 8,
                'transmission_id' => 9,
                'fuel_type_id' => 10,
                'drivetrain_id' => 11,
                'colour' => 'blue',
                'price' => 8900,
                'monthly_price' => 120,
                'deposit' => 3000,
                'door_count' => 4,
                'seats' => 5,
                'manufacture_year' => 2020,
                'odometer_mi' => 100000,
                'engine_size_cc' => 1990,
                'co2' => 112,
                'mpg' => 44.4,
                'previous_owner_count' => 2,
                'first_registration_date' => '2020-01-03',
            ]);

        $vehicle->refresh();
        $this->assertEquals('AB12 CSW', $vehicle->registration_number);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.vehicles.update', $vehicle), [
                'title' => 'lorem ipsum',
                'derivative' => 'foo baz bar',
                'registration_number' => 'NW34LIO',
                'is_published' => true,
                'primary_media' => null,
                'make_id' => 6,
                'model_id' => 7,
                'dealership_id' => 8,
                'transmission_id' => 9,
                'fuel_type_id' => 10,
                'drivetrain_id' => 11,
                'colour' => 'blue',
                'price' => 8900,
                'monthly_price' => 120,
                'deposit' => 3000,
                'door_count' => 4,
                'seats' => 5,
                'manufacture_year' => 2020,
                'odometer_mi' => 100000,
                'engine_size_cc' => 1990,
                'co2' => 112,
                'mpg' => 44.4,
                'previous_owner_count' => 2,
                'first_registration_date' => '2020-01-03',
            ]);

        $response->assertStatus(200);

        $vehicle->refresh();
        $this->assertEquals('NW34 LIO', $vehicle->registration_number);
    }

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

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

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

    public function testGetFinance()
    {
        Queue::fake();
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Finance',
            'group' => 'CodeWeavers',
            'name' => 'CodeWeavers Finance Enabled',
            'config_key' => 'finance-codeweavers-enabled',
            'min_tier' => 'standard',
            'type' => 'boolean',
            'value' => true,
        ]);

        $vehicle = Vehicle::factory()->create(['slug' => 'foo']);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.get-finance', $vehicle), [
                'term' => 60,
                'annual_mileage' => 10000,
                'deposit' => 10,
                'credit_rating' => 'excellent',
            ]);

        $response->assertStatus(200);
        Queue::assertPushed(FetchVehicleFinanceData::class);
    }

    public function testGetCapId()
    {
        $this->seed(VehicleSpecSeeder::class);
        $vehicle = Vehicle::factory()->create(['cap_id' => null]);
        $vehicle2 = Vehicle::factory()->create(['cap_id' => 'abc123']);
        Queue::fake();

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.get-cap-id', $vehicle));

        $response->assertStatus(422);

        Settings::update('vehicle-spec-providers-cap-enabled', true);
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.get-cap-id', $vehicle));
        $response->assertStatus(200);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.get-cap-id', $vehicle2));
        $response->assertStatus(422);
        Queue::assertPushed(VehicleCapIdLookup::class);
    }

    public function testUpdateStatus()
    {
        $vehicle = Vehicle::factory()->create();
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.set-status', $vehicle), [
                'state' => 'published',
            ]);

        $response->assertStatus(200);
        $vehicle->refresh();
    }

    public function testGetImages()
    {
        $vehicle = Vehicle::factory()->create();
        Queue::fake();

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.get-images', $vehicle));

        $response->assertStatus(200);
        Queue::assertNothingPushed();
    }

    public function testGetFeatured()
    {
        Vehicle::factory(3)->create(['featured' => true, 'is_published' => true]);
        Vehicle::factory(5)->create(['featured' => false, 'is_published' => true]);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.vehicles.get-featured'));
        $response->assertStatus(200);

        $this->assertIsArray($response->json('data'));
        $this->assertCount(3, $response->json('data'));
    }

    public function testSetFeatured()
    {
        $to_set_featured = Vehicle::factory(3)->create(['featured' => false, 'is_published' => true]);
        Vehicle::factory(5)->create(['featured' => false, 'is_published' => true]);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.vehicles.set-featured'), [
                'vehicle_ids' => $to_set_featured->pluck('id')
            ]);

        $response->assertStatus(200);

        $this->assertIsArray($response->json('data'));
        $this->assertCount(3, $response->json('data'));
        $this->assertEquals(3, Vehicle::query()->where('featured', 1)->count());

        $featured_ids = collect($response->json('data'))->pluck('id');
        $this->assertEquals($to_set_featured->pluck('id')->toArray(), $featured_ids->toArray());
    }
    public function testExport()
    {
        Vehicle::factory(10)->create();

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.export'));

        $response->assertStatus(200);
        $response->assertDownload();
    }

    public function testImport()
    {
        Queue::fake();
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.import'), [
                'type' => '',
                'file' => UploadedFile::fake()->create('vehicle-import.xlsx'),
            ]);

        $response->assertStatus(200);
        Queue::assertPushed(BulkImportJob::class);
    }

    public function testVehicleImport()
    {
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.import'), [
                'type' => 'default',
                'file' => UploadedFile::fake()
                    ->createWithContent('vehicle-import.xlsx', File::get(dirname(__DIR__) . '/data/vehicle-import.xlsx')),
            ]);

        $response->assertStatus(200);
        $this->assertEquals(3, Vehicle::query()->count());
    }

    public function testVehicleImportViaImportMap()
    {
        $importMap = ImportMap::query()
            ->create([
                'name' => 'foo',
                'slug' => 'foo',
                'type' => 'vehicle',
                'data' => [
                    'sub_field_delimiter' => ';',
                    'has_header_row' => true
                ],
            ]);

        $importMap->fields()->create([
            'on_file' => 'is_published',
            'on_model' => 'is_published',
        ]);
        $importMap->fields()->create([
            'on_file' => 'registration_number',
            'on_model' => 'registration_number',
        ]);
        $importMap->fields()->create([
            'on_file' => 'slug',
            'on_model' => 'slug',
        ]);
        $importMap->fields()->create([
            'on_file' => 'features',
            'on_model' => 'features',
        ]);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.import'), [
                'type' => $importMap->id,
                'file' => UploadedFile::fake()
                    ->createWithContent('vehicle-import.xlsx', File::get(dirname(__DIR__) . '/data/vehicle-import.xlsx')),
            ]);

        $response->assertStatus(200);
        $this->assertEquals(3, Vehicle::query()->count());
        $this->assertEquals(3, Vehicle::query()->with('features')->where('registration_number', 'MBW6 36N')->firstOrFail()?->features->count());
    }

    public function testCopy()
    {
        $vehicle = Vehicle::factory()->create(['title' => 'foo']);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.vehicles.copy', $vehicle->id));

        $response->assertStatus(201);
        $this->assertTrue(Vehicle::query()->where('id', $response->json('data.vehicle.id'))->exists());
        $this->assertNotEquals($vehicle->id, $response->json('data.vehicle.id'));
        $this->assertEquals($vehicle->title, $response->json('data.vehicle.title'));
    }
}
