<?php

namespace Tests\Tenant;

use App\Facades\Settings;
use App\Http\Resources\VehicleViewResource;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Setting;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleAttribute;
use Mtc\MercuryDataModels\VehicleAttributeValue;
use Mtc\MercuryDataModels\VehicleFinance;
use Tests\TenantTestCase;
use Tests\UserForTenant;

class VehicleAttributeTest extends TenantTestCase
{
    use UserForTenant;

    public function testVehicleAttributes()
    {
        $vehicle = Vehicle::factory()->create();

        $this->assertNotEmpty($vehicle);

        // try setting attribute
        $vehicle_attribute = VehicleAttribute::query()
            ->firstOrCreate(
                [
                    'model' => 'vehicle',
                    'name' => 'lez_compliant',
                ],
                [
                    'slug' => 'lez_compliant',
                    'type' => 'boolean',
                    'validation' => [],
                    'data' => []
                ]
            );

        // assert that vehicle doesn't have any attributes
        $example_vehicle_lez_compliant = $vehicle->attributeValues()->where(
            'attribute_id',
            '=',
            $vehicle_attribute->id
        )->first();

        $this->assertEmpty($example_vehicle_lez_compliant);

        // add an attribute value for the vehicle
        $vehicle_attribute_value = VehicleAttributeValue::query()->updateOrCreate(
            [
                'attribute_id' => $vehicle_attribute->id,
                'owner_id' => $vehicle->id,
                'owner_type' => 'vehicle',
            ],
            [
                'value' => true
            ]
        );

        $this->assertEquals(1, VehicleAttributeValue::count());
        $this->assertEquals($vehicle->id, $vehicle_attribute_value->owner_id);
        $this->assertEquals($vehicle_attribute->id, $vehicle_attribute_value->attribute_id);
        $this->assertGreaterThan(0, $vehicle->attributeValues->count());

        // check that looking up the vehicle attribute works as expected
        $example_vehicle_lez_compliant = $vehicle->attributeValues()
            ->whereHas('attribute', function ($query) {
                $query->where('name', '=', 'lez_compliant');
            })->first();

        $this->assertNotEmpty($example_vehicle_lez_compliant);
        $this->assertEquals('lez_compliant', $example_vehicle_lez_compliant->attribute->name);
        $this->assertEquals(1, $example_vehicle_lez_compliant->value);

        // ensure that asking for values for missing vehicle attributes is handled gracefully
        $test_missing_value = $vehicle->attributeValues()
            ->whereHas('attribute', function ($query) {
                $query->where('name', '=', 'missing_value');
            })->first();

        $this->assertNull($test_missing_value);

        $vehicle_resource_array = new VehicleViewResource(Vehicle::factory()->create())->toArray(request());
        $extras = $vehicle_resource_array['vehicle']['extra'];
        $lez_compliant = collect($extras)->where('name', '=', 'lez_compliant');
        $missing_value = collect($extras)->where('name', '=', 'my_missing_value');

        $this->assertFalse($lez_compliant->isEmpty());
        $this->assertTrue($missing_value->isEmpty());
    }

    public function testVehicleResource()
    {
        $vehicle_resource_array = new VehicleViewResource(Vehicle::factory()->create())->toArray(null);
        $extras = $vehicle_resource_array['vehicle']['extra'];
        $lez_compliant = collect($extras)->where('name', '=', 'lez_compliant');

        $this->assertEmpty($lez_compliant);
    }

    public function testSavingsLoaded()
    {
        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-enabled',
            'value' => true,
            'type' => 'bool',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-enabled-previous-price',
            'value' => true,
            'type' => 'bool',
        ]);

        $this->assertEquals(true, Settings::get('vehicle-savings-enabled'));

        $vehicle = Vehicle::factory()->create([
            'price' => 11000,
            'original_price' => 12000,
        ]);

        $vehicle_resource_array = new VehicleViewResource($vehicle)->toArray(null);

        $this->assertArrayHasKey('vehicle', $vehicle_resource_array);
        $this->assertArrayHasKey('extra', $vehicle_resource_array['vehicle']);

        $this->assertTrue(collect($vehicle_resource_array['vehicle']['extra'])
                ->where('slug', 'saving_original_price')->count() > 0);
        $this->assertEquals(1000, collect($vehicle_resource_array['vehicle']['extra'])
            ->where('slug', 'saving_original_price')->first()['value'][0]['value']);
    }

    public function testPVSavingsLoaded()
    {
        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-enabled',
            'value' => true,
            'type' => 'bool',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-enabled-previous-price',
            'value' => true,
            'type' => 'bool',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-type',
            'value' => 'peter-vardy',
            'type' => 'string',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-enabled-finance-saving',
            'value' => true,
            'type' => 'bool',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-pv-finance-saving-pounds-per-thousand',
            'value' => 25,
            'type' => 'int',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-enabled-finance-contribution',
            'value' => true,
            'type' => 'bool',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-pv-finance-contribution-percent-ice',
            'value' => 2,
            'type' => 'int',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-pv-finance-contribution-percent-electric',
            'value' => 5,
            'type' => 'int',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-pv-finance-contribution-max-ice',
            'value' => 600,
            'type' => 'int',
        ]);

        Settings::make([
            'tab' => 'foo-baz',
            'section' => 'lorem',
            'group' => 'ipsum',
            'name' => 'dolor',
            'config_key' => 'vehicle-savings-pv-finance-contribution-max-electric',
            'value' => 1000,
            'type' => 'int',
        ]);

        $this->assertEquals(true, Settings::get('vehicle-savings-enabled'));

        Dealership::factory()->create([
            'id' => 1,
            'location_stock' => 'VE',
            'name' => 'Carz Edinburgh'
        ]);

        $make = VehicleMake::factory()->create([
            'name' => 'Audi'
        ]);

        $vehicle = Vehicle::factory()->create([
            'dealership_id' => 1,
            'type' => 'car',
            'price' => 11000,
            'original_price' => 12000,
            'previous_price' => 12000,
            'rrp_price' => 0,
            'is_vat_applicable' => true,
            'first_registration_date' => date('Y-m-d', strtotime('-24 months'))
        ]);

        $finance_quote = VehicleFinance::factory()->create([
            'vehicle_id' => $vehicle->id,
            'monthly_price' => 200,
            'total_credit_amount' => 91000
        ]);

        $vehicle->make_id = $make->id;
        $vehicle->type = 'car';
        $vehicle->save();
        $vehicle->refresh();

        $expected_finance_rate_saving = 25 * ($finance_quote->total_credit_amount / 1000);

        // capped at £600 for combustion engines
        $expected_finance_contribution = 600;

        $vehicle_resource_array = new VehicleViewResource($vehicle)->toArray(null);

        $extra = collect($vehicle_resource_array['vehicle']['extra']);

        $this->assertTrue($extra->where('slug', 'saving_original_price')->count() > 0);
        $this->assertTrue($extra->where('slug', 'saving_rrp')->count() > 0);
        $this->assertTrue($extra->where('slug', 'saving_finance_rate')->count() > 0);
        $this->assertTrue($extra->where('slug', 'saving_finance_contribution')->count() > 0);

        $this->assertEquals(1000, $extra->where('slug', 'saving_original_price')->first()['value'][0]['value']);
        $this->assertNull($extra->where('slug', 'saving_rrp')->first()['value'][0]['value']);
        $this->assertNotNull($extra->where('slug', 'saving_finance_rate')->first()['value'][0]['value']);
        $this->assertNotNull($extra->where('slug', 'saving_finance_contribution')->first()['value'][0]['value']);

        $this->assertEquals(
            $expected_finance_rate_saving,
            $extra->where('slug', 'saving_finance_rate')->first()['value'][0]['value']
        );
        $this->assertEquals(
            $expected_finance_contribution,
            $extra->where('slug', 'saving_finance_contribution')->first()['value'][0]['value']
        );
    }

    public function testVehicleAttributeCanAddSortableSetting()
    {
        $slug = 'sortable';
        $vehicleAttribute = VehicleAttribute::query()
            ->firstOrCreate(
                [
                    'name' => 'Sortable Boolean Attribute',
                ],
                [
                    'type' => 'boolean',
                    'slug' => $slug,
                    'validation' => [],
                    'data' => [],
                    'sortable' => false
                ]
            );

        $vehicleAttribute->sortable = true;
        $vehicleAttribute->save();

        $setting = Settings::get('automotive-vehicle-sorting-' . $slug);

        $this->assertNotNull($setting);
        $this->assertTrue($setting);
    }

    public function testVehicleAttributeCanAddFilterableSetting()
    {
        $slug = 'sortable';
        $vehicleAttribute = VehicleAttribute::query()
            ->firstOrCreate(
                [
                    'name' => 'Filterable Boolean Attribute',
                ],
                [
                    'type' => 'boolean',
                    'slug' => $slug,
                    'validation' => [],
                    'data' => [],
                    'filterable' => false
                ]
            );

        $vehicleAttribute->filterable = true;
        $vehicleAttribute->save();

        $setting = Settings::get('automotive-vehicle-filters-'.$slug);

        $this->assertNotNull($setting);
        $this->assertTrue($setting);
    }

    public function testChangingTextToIntegerCastsCorrectly(): void
    {
        $attribute = VehicleAttribute::factory()->create([
            'name' => 'Seatbelts',
            'slug' => 'seatbelts',
            'type' => 'text',
            'model' => 'vehicle',
        ]);

        $seatbelts = [4, 2, 5];
        $valueIds = [];

        foreach ($seatbelts as $seatbelt) {
            $valueIds[] = VehicleAttributeValue::factory()->create([
                'attribute_id' => $attribute->id,
                'owner_id' => Vehicle::factory()->create()->id,
                'owner_type' => 'vehicle',
                'type' => 'text',
                'value_text' => (string) $seatbelt,
            ])->id;
        }

        $attribute->type = 'integer';
        $attribute->save();

        foreach ($seatbelts as $i => $seatbelt) {
            $value = VehicleAttributeValue::find($valueIds[$i]);
            $this->assertEquals('integer', $value->type);
            $this->assertEquals($seatbelt, $value->value_integer);
            $this->assertEquals((string) $seatbelt, $value->value_text); // original preserved
        }
    }

    public function testChangingTextToNumberCastsCorrectly(): void
    {
        $attribute = VehicleAttribute::factory()->create([
            'name' => 'height',
            'slug' => 'height',
            'type' => 'text',
            'model' => 'vehicle',
        ]);

        $heights = ['1.6' => 1.6, '2.0' => 2.0, '3.5' => 3.5];
        $valueIds = [];

        foreach (array_keys($heights) as $height) {
            $valueIds[$height] = VehicleAttributeValue::factory()->create([
                'attribute_id' => $attribute->id,
                'owner_id' => Vehicle::factory()->create()->id,
                'owner_type' => 'vehicle',
                'type' => 'text',
                'value_text' => $height,
            ])->id;
        }

        $attribute->type = 'number';
        $attribute->save();

        foreach ($heights as $text => $expected) {
            $value = VehicleAttributeValue::find($valueIds[$text]);
            $this->assertEquals('number', $value->type);
            $this->assertEquals($expected, $value->value_float);
            $this->assertEquals($text, $value->value_text); // original preserved
        }
    }

    public function testChangingTextToIntegerPreservesOriginalTextValue(): void
    {
        $vehicle = Vehicle::factory()->create();

        $attribute = VehicleAttribute::factory()->create([
            'name' => 'Year',
            'slug' => 'year',
            'type' => 'text',
            'model' => 'vehicle',
        ]);

        $value = VehicleAttributeValue::factory()->create([
            'attribute_id' => $attribute->id,
            'owner_id' => $vehicle->id,
            'owner_type' => 'vehicle',
            'type' => 'text',
            'value_text' => '2019',
        ]);

        $attribute->type = 'integer';
        $attribute->save();

        $value->refresh();

        $this->assertEquals('integer', $value->type);
        $this->assertEquals(2019, $value->value_integer);
        $this->assertEquals('2019', $value->value_text); // original column left intact as audit trail
    }

    public function testChangingNumberToTextCastsCorrectly(): void
    {
        $attribute = VehicleAttribute::factory()->create([
            'name' => 'Boot Space',
            'slug' => 'boot-space',
            'type' => 'number',
            'model' => 'vehicle',
        ]);

        $volumes = [1.6, 2.0, 3.5];
        $valueIds = [];

        foreach ($volumes as $volume) {
            $valueIds[] = VehicleAttributeValue::factory()->create([
                'attribute_id' => $attribute->id,
                'owner_id' => Vehicle::factory()->create()->id,
                'owner_type' => 'vehicle',
                'type' => 'number',
                'value_float' => $volume,
                'value_text' => null,
            ])->id;
        }

        $attribute->type = 'text';
        $attribute->save();

        foreach ($volumes as $i => $volume) {
            $value = VehicleAttributeValue::find($valueIds[$i]);
            $this->assertEquals('text', $value->type);
            $this->assertNotNull($value->value_text, "value_text should be migrated from value_float");
            $this->assertEquals($volume, $value->value_float); // original preserved
        }
    }

    public function testChangingIntegerToTextCastsCorrectly(): void
    {
        $attribute = VehicleAttribute::factory()->create([
            'name' => 'Doors',
            'slug' => 'doors',
            'type' => 'integer',
            'model' => 'vehicle',
        ]);

        $doors = [2, 4, 5];
        $valueIds = [];

        foreach ($doors as $count) {
            $valueIds[] = VehicleAttributeValue::factory()->create([
                'attribute_id' => $attribute->id,
                'owner_id' => Vehicle::factory()->create()->id,
                'owner_type' => 'vehicle',
                'type' => 'integer',
                'value_integer' => $count,
                'value_text' => null,
            ])->id;
        }

        $attribute->type = 'text';
        $attribute->save();

        foreach ($doors as $i => $count) {
            $value = VehicleAttributeValue::find($valueIds[$i]);
            $this->assertEquals('text', $value->type);
            $this->assertEquals((string) $count, $value->value_text);
            $this->assertEquals($count, $value->value_integer); // original preserved
        }
    }
}
