<?php

namespace Tests\Feature;

use App\Console\Commands\SetKeyloopMakesAndModels;
use App\Facades\Settings;
use App\Modules\Leasing\Jobs\FlagCheapestVariantsJob;
use App\Modules\Leasing\Jobs\GenerateFinanceJob;
use App\Modules\Leasing\Jobs\PurgeOldLeaseDataJob;
use App\Modules\Leasing\KeyloopFleetKompact;
use Database\Seeders\Tenant\KeyloopFleetKompactSeeder;
use Database\Seeders\Tenant\KeyloopLeaseFtpSettingSeeder;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Support\Facades\Storage;
use Mtc\MercuryDataModels\BodyStyleType;
use Mtc\MercuryDataModels\FuelType;
use Mtc\MercuryDataModels\KeyloopLeaseDiscount;
use Mtc\MercuryDataModels\KeyloopLeaseOption;
use Mtc\MercuryDataModels\KeyloopLeaseOptionCategory;
use Mtc\MercuryDataModels\KeyloopLeaseResidualValue;
use Mtc\MercuryDataModels\KeyloopLeaseVehicleVariant;
use Mtc\MercuryDataModels\Leasing\KearysLeaseFinanceCalculator;
use Mtc\MercuryDataModels\TransmissionType;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\VehicleModel;
use Tests\TestCase;
use Tests\UserForTenant;

class KeyloopFleetKompactTest extends TestCase
{
    use DatabaseMigrations;
    use UserForTenant;

    protected $tenancy = true;

    public function testIsEnabled()
    {
        $this->seed(KeyloopFleetKompactSeeder::class);
        $this->assertFalse((new KeyloopFleetKompact())->enabled());
        Settings::update('leasing-keyloop-fleet-kompact-enabled', true);
        $this->assertTrue((new KeyloopFleetKompact())->enabled());
    }

    public function testImport()
    {
        $this->seed(KeyloopFleetKompactSeeder::class);
        Settings::update('leasing-keyloop-fleet-kompact-enabled', true);
        $this->setupDisks();
        $this->setupData();

        (new KeyloopFleetKompact())->runScheduledImport();

        // assert that we persisted the expected number of vehicles
        $this->assertCount(5, KeyloopLeaseVehicleVariant::all());
        $this->assertCount(0, KeyloopLeaseVehicleVariant::query()->where('name', '=', 'foo')->get());
        $this->assertCount(5, KeyloopLeaseVehicleVariant::query()->where('type', '=', 'car')->get());

        // assert that we deleted the file from local disk
        $this->assertFalse(Storage::disk('local')->exists('JATO_DDvaria.csv'));

        // update the vehicles
        KeyloopLeaseVehicleVariant::query()->update([
            'name' => 'foo',
        ]);

        $this->assertCount(5, KeyloopLeaseVehicleVariant::query()->where('name', '=', 'foo')->get());

        // run the import to revert the update
        (new KeyloopFleetKompact())->runScheduledImport();

        // assert that we reverted the changes
        $this->assertCount(5, KeyloopLeaseVehicleVariant::all());
        $this->assertCount(5, KeyloopLeaseVehicleVariant::query()->where('type', '=', 'car')->get());
        $this->assertCount(0, KeyloopLeaseVehicleVariant::query()->where('name', '=', 'foo')->get());
        $this->assertCount(0, KeyloopLeaseVehicleVariant::query()->whereNull('transmission_id')->get());

        $this->testVariantSlugs();
        $this->testResidualValueImport();
        $this->testOptionImport();
        $this->testDiscounts();
    }

    public function testMakeCodeCommand()
    {
        \App\Master\Models\VehicleMake::factory()->create([
            'name' => 'Audi',
            'keyloop_id' => null,
        ]);

        \App\Master\Models\VehicleMake::factory()->create([
            'name' => 'Land Rover',
            'keyloop_id' => null,
        ]);

        $this->assertCount(0, \App\Master\Models\VehicleMake::query()->where('keyloop_id', '=', 'AUD')->get());
        $this->assertCount(0, \App\Master\Models\VehicleMake::query()->where('keyloop_id', '=', 'LAN')->get());

        (new SetKeyloopMakesAndModels())->handle();

        $this->assertCount(1, \App\Master\Models\VehicleMake::query()->where('keyloop_id', '=', 'AUD')->get());
        $this->assertCount(1, \App\Master\Models\VehicleMake::query()->where('keyloop_id', '=', 'LAN')->get());
    }

    public function testModelCodeCommand()
    {
        \App\Master\Models\VehicleModel::factory()->create([
            'name' => '1 Series',
            'keyloop_id' => null,
        ]);

        \App\Master\Models\VehicleModel::factory()->create([
            'name' => 'S Class',
            'keyloop_id' => null,
        ]);

        \App\Master\Models\VehicleModel::factory()->create([
            'name' => 'Range Rover Sport',
            'keyloop_id' => null,
        ]);

        $this->assertCount(0, \App\Master\Models\VehicleModel::query()->whereNotNull('keyloop_id')->get());

        (new SetKeyloopMakesAndModels())->handle();

        $this->assertCount(1, \App\Master\Models\VehicleModel::query()->where('keyloop_id', '=', 'SERIES 1')->get());
        $this->assertCount(1, \App\Master\Models\VehicleModel::query()->where('keyloop_id', '=', 'S-CLASS')->get());
        $this->assertCount(1, \App\Master\Models\VehicleModel::query()->where('keyloop_id', '=', 'RANGE ROVER SPO')->get());
    }

    public function testBlockedMakes()
    {
        $this->seed(KeyloopFleetKompactSeeder::class);
        $this->seed(KeyloopLeaseFtpSettingSeeder::class);
        Settings::update('leasing-keyloop-fleet-kompact-enabled', true);
        $this->setupDisks(true);

        $make_audi = VehicleMake::factory()->create([
            'name' => 'Audi',
            'keyloop_id' => 'AUD',
            'keyloop_import_enabled' => true,
        ]);

        $make_bmw = VehicleMake::factory()->create([
            'name' => 'BMW',
            'keyloop_id' => 'BMW',
            'keyloop_import_enabled' => false,
        ]);

        VehicleModel::factory()->create([
            'name' => 'A1',
            'keyloop_id' => 'A1',
            'keyloop_import_enabled' => true,
            'make_id' => $make_audi->id,
        ]);

        VehicleModel::factory()->create([
            'name' => 'A1',
            'keyloop_id' => 'A1',
            'keyloop_import_enabled' => true,
            'make_id' => $make_audi->id,
        ]);

        VehicleModel::factory()->create([
            'name' => 'A3',
            'keyloop_id' => 'A3',
            'keyloop_import_enabled' => true,
            'make_id' => $make_audi->id,
        ]);

        VehicleModel::factory()->create([
            'name' => '3 Series',
            'keyloop_id' => 'SERIES 3',
            'keyloop_import_enabled' => true,
            'make_id' => $make_bmw->id,
        ]);

        (new KeyloopFleetKompact())->runScheduledImport();

        // assert that we reverted the changes
        $this->assertCount(4, KeyloopLeaseVehicleVariant::all());
        $this->assertCount(
            4,
            KeyloopLeaseVehicleVariant::query()
                ->where('make_id', '=', $make_audi->id)
                ->get()
        );
    }

    public function testBlockedModels()
    {
        $this->seed(KeyloopFleetKompactSeeder::class);
        Settings::update('leasing-keyloop-fleet-kompact-enabled', true);
        $this->setupDisks(true);

        $make_audi = VehicleMake::factory()->create([
            'name' => 'Audi',
            'keyloop_id' => 'AUD',
            'keyloop_import_enabled' => true,
        ]);

        $make_bmw = VehicleMake::factory()->create([
            'name' => 'BMW',
            'keyloop_id' => 'BMW',
            'keyloop_import_enabled' => true,
        ]);

        $model_a1 = VehicleModel::factory()->create([
            'name' => 'A1',
            'keyloop_id' => 'A1',
            'keyloop_import_enabled' => true,
            'make_id' => $make_audi->id,
        ]);

        VehicleModel::factory()->create([
            'name' => '3 Series',
            'keyloop_id' => 'SERIES 3',
            'keyloop_import_enabled' => false,
            'make_id' => $make_bmw->id,
        ]);

        (new KeyloopFleetKompact())->runScheduledImport();

        // assert that we reverted the changes
        $this->assertCount(4, KeyloopLeaseVehicleVariant::all());

        $this->assertCount(
            4,
            KeyloopLeaseVehicleVariant::query()
                ->where('make_id', '=', $make_audi->id)
                ->where('model_id', '=', $model_a1->id)
                ->get()
        );
    }

    private function testResidualValueImport()
    {
        $this->assertCount(5, KeyloopLeaseVehicleVariant::all());
        $this->assertCount(96, KeyloopLeaseResidualValue::all());

        // assert that expected vehicles get expected residual values
        $this->assertCount(
            48,
            KeyloopLeaseVehicleVariant::query()
                ->where('external_variant_id', '=', '821361620220101')
                ->first()
                ?->residualValues);

        // assert that expected vehicles get expected residual values
        $this->assertCount(
            48,
            KeyloopLeaseVehicleVariant::query()
                ->where('external_variant_id', '=', '829745120220101')
                ->first()
                ?->residualValues);

        $residual_values = KeyloopLeaseVehicleVariant::query()
            ->where('external_variant_id', '=', '821361620220101')
            ->first()
            ?->residualValues
            ->where('contract_length', '=', 12)
            ->where('annual_mileage', '=', 10000)
            ->first();

        $this->assertEquals(22301.4, $residual_values->residual_value_including_vat);


    }

    private function testOptionImport()
    {
        // assert that we imported the exected categories
        $this->assertCount(4, KeyloopLeaseOptionCategory::all());

        // assert that the category mappings work
        $hill_hold = KeyloopLeaseOption::query()->where('option_code', '=', '43201')->first();

        $this->assertNotNull($hill_hold);
        $this->assertNotNull($hill_hold->category);
        $this->assertEquals('Brakes', $hill_hold->category->name);

        $vehicle = KeyloopLeaseVehicleVariant::query()->where('external_variant_id', '=', '829745120220101')->first();

        // check that the price was set correctly
        $this->assertEquals(57127.967, $vehicle->price_inc_vat_inc_vrt);

        $this->assertCount(1, $vehicle->spec);
        $this->assertCount(8, $vehicle->options);
        $this->assertCount(4, $vehicle->getColours());
        $this->assertCount(9, $vehicle->getTrims());

        $sunroof = $vehicle->options->where('option_code', '=', 'S0402')->first();

        $this->assertNotNull($sunroof);
        $this->assertEquals('Panoramic Glass Sunroof', $sunroof->name);
        $this->assertEquals(1622.3727, $sunroof->price->price_inc_vat_inc_vrt);

        $colour = $vehicle->getColours()->filter(fn ($colour) => $colour->colour_code ==  'P0475')->first();

        $this->assertEquals('Black Sapphire Metallic', $colour->name);
        $this->assertEquals(968.1525, $colour->price->price_inc_vat_inc_vrt);
    }

    private function testDiscounts()
    {
        $this->assertCount(4, KeyloopLeaseDiscount::all());

        $bmw = KeyloopLeaseVehicleVariant::query()->where('external_variant_id', '=', '829745120220101')->first();
        $this->assertCount(1, $bmw->getDiscounts('cash'));
        $this->assertEquals('cash', $bmw->getDiscounts()->first()->discount_type);
        $this->assertEquals(99, $bmw->getDiscounts()->first()->discount_amount);

        $audi = KeyloopLeaseVehicleVariant::query()->where('external_variant_id', '=', '821361620220101')->first();
        $this->assertCount(1, $audi->getDiscounts('percentage'));
        $this->assertEquals('percentage', $audi->getDiscounts()->first()->discount_type);
        $this->assertEquals(10, $audi->getDiscounts()->first()->discount_amount);
    }

    private function testVariantSlugs()
    {
        $this->assertCount(5, KeyloopLeaseVehicleVariant::all());
        $this->assertCount(5, KeyloopLeaseVehicleVariant::query()->whereNotNull('slug')->get());
        $this->assertCount(1, KeyloopLeaseVehicleVariant::query()->where('slug', 'audi-a1')->get());
        $this->assertCount(1, KeyloopLeaseVehicleVariant::query()->where('slug', 'audi-a1-1')->get());
        $this->assertCount(1, KeyloopLeaseVehicleVariant::query()->where('slug', 'audi-a1-2')->get());
        //$this->assertCount(1, KeyloopLeaseVehicleVariant::query()->where('slug', 'audi-a1-3')->get());
    }

    public function testFinanceGeneration()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Leasing',
            'group' => 'Keyloop Fleet Kompact',
            'name' => 'Enabled',
            'config_key' => 'leasing-keyloop-fleet-kompact-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Leasing',
            'group' => 'Keyloop Fleet Kompact',
            'name' => 'Provider Type',
            'config_key' => 'leasing-keyloop-fleet-kompact-provider-type',
            'type' => 'string',
            'value' => 'kearys',
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Leasing',
            'group' => 'Keyloop Fleet Kompact',
            'name' => 'VAT percentage in decimal',
            'description' => 'For use in finance calculations. Decimal representation of a percentage. 7% should be entered as 0.07',
            'config_key' => 'leasing-keyloop-fleet-kompact-vat-rate',
            'type' => 'number',
            'value' => 0.23,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Leasing',
            'group' => 'Keyloop Fleet Kompact',
            'name' => 'Interest rate percentage in decimal',
            'description' => 'For use in finance calculations. Decimal representation of a percentage. 7% should be entered as 0.07',
            'config_key' => 'leasing-keyloop-fleet-kompact-interest-rate',
            'type' => 'number',
            'value' => 0.07,
        ]);

        $variant = KeyloopLeaseVehicleVariant::factory()->create([
            'price_ex_vat_ex_vrt' => 34766.33,
            'delivery_ex_vat_ex_vrt' => 0,
            'vehicle_vrt' => 0,
            'finance_recently_updated' => false,
            'cheapest_monthly_price_ex_vat' => 0,
            'cheapest_monthly_price_inc_vat' => 0,
        ]);

        KeyloopLeaseResidualValue::factory()->create([
            'external_variant_id' => $variant->external_variant_id,
            'contract_length' => 36,
            'annual_mileage' => 15000,
            'residual_value_excluding_vat' => 11871.43,
            'residual_value_including_vat' => 11871.43 * 1.23,
        ]);

        (new GenerateFinanceJob())->handle();

        $this->assertCount(1, KeyloopLeaseVehicleVariant::query()->where('finance_recently_updated', true)->get());

        $variant->refresh();
        $this->assertTrue($variant->finance_recently_updated);
        $this->assertEquals(732.24, $variant->cheapest_monthly_price_ex_vat);
        $this->assertEquals(900.66, $variant->cheapest_monthly_price_inc_vat);
    }

    public function testPurgeOldData()
    {
        $this->seed(KeyloopFleetKompactSeeder::class);
        Settings::update('leasing-keyloop-fleet-kompact-enabled', true);

        KeyloopLeaseVehicleVariant::factory(3)->create([
            'was_recently_synced' => false,
        ]);

        KeyloopLeaseVehicleVariant::factory(5)->create([
            'was_recently_synced' => true,
        ]);

        (new PurgeOldLeaseDataJob())->handle();

        $this->assertCount(5, KeyloopLeaseVehicleVariant::all());
    }

    public function testCheapestVariants()
    {
        $this->seed(KeyloopFleetKompactSeeder::class);
        Settings::update('leasing-keyloop-fleet-kompact-enabled', true);

        KeyloopLeaseVehicleVariant::factory()->create([
            'was_recently_synced' => true,
            'is_cheapest_variant' => false,
            'price_ex_vat_ex_vrt' => 111,
            'model_id' => 1,
        ]);

        KeyloopLeaseVehicleVariant::factory()->create([
            'was_recently_synced' => true,
            'is_cheapest_variant' => false,
            'price_ex_vat_ex_vrt' => 222,
            'model_id' => 1,
        ]);

        KeyloopLeaseVehicleVariant::factory()->create([
            'was_recently_synced' => true,
            'is_cheapest_variant' => false,
            'price_ex_vat_ex_vrt' => 333,
            'model_id' => 1,
        ]);

        KeyloopLeaseVehicleVariant::factory()->create([
            'was_recently_synced' => true,
            'is_cheapest_variant' => false,
            'price_ex_vat_ex_vrt' => 999,
            'model_id' => 2,
        ]);

        KeyloopLeaseVehicleVariant::factory()->create([
            'was_recently_synced' => true,
            'is_cheapest_variant' => false,
            'price_ex_vat_ex_vrt' => 888,
            'model_id' => 2,
        ]);

        KeyloopLeaseVehicleVariant::factory()->create([
            'was_recently_synced' => true,
            'is_cheapest_variant' => false,
            'price_ex_vat_ex_vrt' => 777,
            'model_id' => 2,
        ]);

        (new FlagCheapestVariantsJob())->handle();

        $this->assertEquals(
            111,
            KeyloopLeaseVehicleVariant::query()
                ->where('is_cheapest_variant', true)
                ->where('model_id', 1)
                ->pluck('price_ex_vat_ex_vrt')
                ->first()
        );

        $this->assertEquals(
            777,
            KeyloopLeaseVehicleVariant::query()
                ->where('is_cheapest_variant', true)
                ->where('model_id', 2)
                ->pluck('price_ex_vat_ex_vrt')
                ->first()
        );
    }

    private function setupDisks($variants_only = false)
    {
        $fake_disk = Storage::fake('autonomy-ftp');
        $local_disk = Storage::fake('local');

        $filenames = [
            'JATO_DDvaria.csv',
        ];

        if (!$variants_only) {
            $filenames = array_merge($filenames, [
                'JATO_RV.csv',
                'JATO_DDcolor.csv',
                'JATO_FQdiscs.csv',
                'option_categories.csv',
                'JATO_DDspecs.csv',
                'JATO_oprel.csv',
                'JATO_DDopprc.csv',
                'JATO_DDoptns.csv',
            ]);
        }

        collect($filenames)->each(function ($filename) {
            $test_file = dirname(__DIR__) . '/data/KeyloopFleetKompact/' . $filename;
            Storage::disk('autonomy-ftp')->put($filename, file_get_contents($test_file));
            $this->assertTrue(Storage::disk('autonomy-ftp')->exists($filename));
        });

        Storage::shouldReceive('build')->with([
            'driver' => 'ftp',
            'host' => Settings::get('leasing-keyloop-fleet-kompact-ftp-host'),
            'username' => Settings::get('leasing-keyloop-fleet-kompact-ftp-username'),
            'password' => Settings::get('leasing-keyloop-fleet-kompact-ftp-password'),
            'ssl' => null,
        ])->andReturn($fake_disk);

        Storage::shouldReceive('disk')->andReturn($local_disk);
    }

    private function setupData()
    {
        $make_audi = VehicleMake::factory()->create([
            'name' => 'Audi',
            'keyloop_id' => 'AUD',
            'keyloop_import_enabled' => true,
        ]);

        $make_bmw = VehicleMake::factory()->create([
            'name' => 'BMW',
            'keyloop_id' => 'BMW',
            'keyloop_import_enabled' => true,
        ]);

        VehicleModel::factory()->create([
            'name' => 'A1',
            'keyloop_id' => 'A1',
            'keyloop_import_enabled' => true,
            'make_id' => $make_audi->id,
        ]);

        VehicleModel::factory()->create([
            'name' => 'A3',
            'keyloop_id' => 'A3',
            'keyloop_import_enabled' => true,
            'make_id' => $make_audi->id,
        ]);

        VehicleModel::factory()->create([
            'name' => '3 Series',
            'keyloop_id' => 'SERIES 3',
            'keyloop_import_enabled' => true,
            'make_id' => $make_bmw->id,
        ]);

        BodyStyleType::factory()->create([
            'name' => 'Saloon',
        ]);

        BodyStyleType::factory()->create([
            'name' => 'Hatchback',
        ]);

        BodyStyleType::factory()->create([
            'name' => 'Estate',
        ]);

        BodyStyleType::factory()->create([
            'name' => 'Coupe',
        ]);

        FuelType::factory()->create([
            'name' => 'Diesel',
        ]);

        FuelType::factory()->create([
            'name' => 'Petrol',
        ]);

        FuelType::factory()->create([
            'name' => 'LPG',
        ]);

        TransmissionType::factory()->create([
            'slug' => 'manual',
            'name' => 'Manual',
        ]);

        TransmissionType::factory()->create([
            'slug' => 'automatic',
            'name' => 'Automatic',
        ]);
    }

    public function testDiscountPercentageValue()
    {
        $variant = KeyloopLeaseVehicleVariant::factory()->create();
        KeyloopLeaseDiscount::query()->create([
            'external_variant_id' => $variant->external_variant_id,
            'make_id' => $variant->make_id,
            'model_id' => $variant->model_id,
            'discount_type' => 'percentage',
            'discount_amount' => 5,
        ]);

        $calculator = new KearysLeaseFinanceCalculator();
        $result = $this->invokeMethod($calculator, 'getDiscountPercentage', [$variant]);
        $this->assertEquals(0.05, $result);
    }

    private function invokeMethod(&$object, string $methodName, array $parameters = [])
    {
        $reflection = new \ReflectionClass(get_class($object));

        $method = $reflection->getMethod($methodName);
        $method->setAccessible(true);

        return $method->invokeArgs($object, $parameters);
    }
}
