<?php

namespace Tests\Tenant;

use App\Events\StockSyncFinished;
use App\Facades\Settings;
use App\Jobs\SalesforceStockExportJob;
use Database\Seeders\Tenant\SettingsDefinitionSeeder;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Str;
use Mtc\MercuryDataModels\Vehicle;
use Tests\TenantTestCase;

class SalesForceTest extends TenantTestCase
{
    public function testIntegrationDisabled()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'foo',
            'config_key' => 'sales-force-inbound-vehicle-notifications-enabled',
            'type' => 'boolean',
            'value' => false,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'Bearer token',
            'config_key' => 'sales-force-inbound-vehicle-notifications-bearer-token',
            'type' => 'secret',
            'value' => 'abc123',
            'validation_rules' => [
                "required_if:sales-force-inbound-vehicle-notifications-enabled,true"
            ]
        ]);

        $response = $this->postJson(route('tenant.notifications.sales-force.notify-vehicle', tenant('id')), [
            'vehicle_id' => 1,
            'price' => 12345,
            'is_published' => true,
            'is_reserved' => true,
            'is_sold' => true,
        ], [
            'Authorization' => 'Bearer abc123',
        ]);

        $response->assertStatus(403);
    }

    public function testNotificationUnauthorised()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'foo',
            'config_key' => 'sales-force-inbound-vehicle-notifications-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'Bearer token',
            'config_key' => 'sales-force-inbound-vehicle-notifications-bearer-token',
            'type' => 'secret',
            'value' => 'abc123',
            'validation_rules' => [
                "required_if:sales-force-inbound-vehicle-notifications-enabled,true"
            ]
        ]);

        $response = $this->postJson(route('tenant.notifications.sales-force.notify-vehicle', tenant('id')), [
            // missing required parameters
        ], [
            'Authorization' => 'Bearer xyz999',
        ]);

        $response->assertStatus(403);
    }

    public function testNotificationEmptyToken()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'foo',
            'config_key' => 'sales-force-inbound-vehicle-notifications-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'Bearer token',
            'config_key' => 'sales-force-inbound-vehicle-notifications-bearer-token',
            'type' => 'secret',
            'value' => '',
            'validation_rules' => [
                "required_if:sales-force-inbound-vehicle-notifications-enabled,true"
            ]
        ]);

        $response = $this->postJson(route('tenant.notifications.sales-force.notify-vehicle', tenant('id')), [
            'vehicle_id' => 1,
            'price' => 12345,
            'is_published' => true,
            'is_reserved' => true,
            'is_sold' => true,
        ], [
            'Authorization' => 'Bearer ',
        ]);

        $response->assertStatus(403);
    }

    public function testNotificationMissingParameters()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'foo',
            'config_key' => 'sales-force-inbound-vehicle-notifications-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'Bearer token',
            'config_key' => 'sales-force-inbound-vehicle-notifications-bearer-token',
            'type' => 'secret',
            'value' => 'abc123',
            'validation_rules' => [
                "required_if:sales-force-inbound-vehicle-notifications-enabled,true"
            ]
        ]);

        $response = $this->postJson(route('tenant.notifications.sales-force.notify-vehicle', tenant('id')), [
            // missing required parameters
        ], [
            'Authorization' => 'Bearer abc123',
        ]);

        $response->assertStatus(422);
        $this->assertArrayHasKey('errors', $response->json());
        $this->assertArrayHasKey('vehicle_id', $response->json('errors'));
        $this->assertArrayHasKey('price', $response->json('errors'));
        $this->assertArrayHasKey('is_published', $response->json('errors'));
        $this->assertArrayHasKey('is_reserved', $response->json('errors'));
        $this->assertArrayHasKey('is_sold', $response->json('errors'));
    }

    public function testNotificationIncorrectParameterTypes()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'foo',
            'config_key' => 'sales-force-inbound-vehicle-notifications-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'Bearer token',
            'config_key' => 'sales-force-inbound-vehicle-notifications-bearer-token',
            'type' => 'secret',
            'value' => 'abc123',
            'validation_rules' => [
                "required_if:sales-force-inbound-vehicle-notifications-enabled,true"
            ]
        ]);

        $response = $this->postJson(route('tenant.notifications.sales-force.notify-vehicle', tenant('id')), [
            'vehicle_id' => 1,
            'price' => 12345,
            'is_published' => 'true',
            'is_reserved' => 'true',
            'is_sold' => 'false',
        ], [
            'Authorization' => 'Bearer abc123',
        ]);

        $response->assertStatus(422);
        $this->assertArrayHasKey('errors', $response->json());
        $this->assertArrayHasKey('is_published', $response->json('errors'));
        $this->assertArrayHasKey('is_reserved', $response->json('errors'));
        $this->assertArrayHasKey('is_sold', $response->json('errors'));
    }

    public function testNotificationGoodRequest()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'foo',
            'config_key' => 'sales-force-inbound-vehicle-notifications-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'Bearer token',
            'config_key' => 'sales-force-inbound-vehicle-notifications-bearer-token',
            'type' => 'secret',
            'value' => 'abc123',
            'validation_rules' => [
                "required_if:sales-force-inbound-vehicle-notifications-enabled,true"
            ]
        ]);

        $response = $this->postJson(route('tenant.notifications.sales-force.notify-vehicle', tenant('id')), [
            'vehicle_id' => 1,
            'price' => 12345,
            'is_published' => true,
            'is_reserved' => true,
            'is_sold' => true,
        ], [
            'Authorization' => 'Bearer abc123',
        ]);

        $response->assertStatus(200);
    }

    public function testVehicleUpdatePriceAndPublished()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'foo',
            'config_key' => 'sales-force-inbound-vehicle-notifications-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'Bearer token',
            'config_key' => 'sales-force-inbound-vehicle-notifications-bearer-token',
            'type' => 'secret',
            'value' => 'abc123',
            'validation_rules' => [
                "required_if:sales-force-inbound-vehicle-notifications-enabled,true"
            ]
        ]);

        $vehicle = Vehicle::factory()->create([
            'uuid' => 'foo',
            'price' => 12345,
            'is_published' => false,
            'is_reserved' => false,
            'is_sold' => false,
        ]);

        $response = $this->postJson(route('tenant.notifications.sales-force.notify-vehicle', tenant('id')), [
            'vehicle_id' => $vehicle->uuid,
            'price' => 555.67,
            'is_published' => true,
            'is_reserved' => false,
            'is_sold' => false,
        ], [
            'Authorization' => 'Bearer abc123',
        ]);

        $response->assertStatus(200);

        $vehicle->refresh();

        $this->assertEquals(555.67, $vehicle->price);
        $this->assertTrue($vehicle->is_published);
        $this->assertFalse($vehicle->is_reserved);
        $this->assertFalse($vehicle->is_sold);
    }

    public function testVehicleUpdateReserved()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'foo',
            'config_key' => 'sales-force-inbound-vehicle-notifications-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'Bearer token',
            'config_key' => 'sales-force-inbound-vehicle-notifications-bearer-token',
            'type' => 'secret',
            'value' => 'abc123',
            'validation_rules' => [
                "required_if:sales-force-inbound-vehicle-notifications-enabled,true"
            ]
        ]);

        $vehicle = Vehicle::factory()->create([
            'uuid' => 'foo',
            'price' => 12345,
            'is_published' => true,
            'is_reserved' => false,
            'is_sold' => false,
        ]);

        $response = $this->postJson(route('tenant.notifications.sales-force.notify-vehicle', tenant('id')), [
            'vehicle_id' => $vehicle->uuid,
            'price' => 12345,
            'is_published' => true,
            'is_reserved' => true,
            'is_sold' => false,
        ], [
            'Authorization' => 'Bearer abc123',
        ]);

        $response->assertStatus(200);

        $vehicle->refresh();

        $this->assertEquals(12345, $vehicle->price);
        $this->assertTrue($vehicle->is_published);
        $this->assertTrue($vehicle->is_reserved);
        $this->assertFalse($vehicle->is_sold);
    }

    public function testVehicleUpdateSold()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'foo',
            'config_key' => 'sales-force-inbound-vehicle-notifications-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'Bearer token',
            'config_key' => 'sales-force-inbound-vehicle-notifications-bearer-token',
            'type' => 'secret',
            'value' => 'abc123',
            'validation_rules' => [
                "required_if:sales-force-inbound-vehicle-notifications-enabled,true"
            ]
        ]);

        $vehicle = Vehicle::factory()->create([
            'uuid' => 'foo',
            'price' => 12345,
            'is_published' => true,
            'is_reserved' => true,
            'is_sold' => false,
        ]);

        $response = $this->postJson(route('tenant.notifications.sales-force.notify-vehicle', tenant('id')), [
            'vehicle_id' => $vehicle->uuid,
            'price' => 12345,
            'is_published' => false,
            'is_reserved' => false,
            'is_sold' => true,
        ], [
            'Authorization' => 'Bearer abc123',
        ]);

        $response->assertStatus(200);

        $vehicle->refresh();

        $this->assertEquals(12345, $vehicle->price);
        $this->assertFalse($vehicle->is_published);
        $this->assertFalse($vehicle->is_reserved);
        $this->assertTrue($vehicle->is_sold);
    }

    public function testOutboundUpdateDisabled()
    {
        Queue::fake();

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'Export Stock',
            'config_key' => 'sales-force-stock-export-enabled',
            'type' => 'boolean',
            'value' => false,
        ]);

        Event::dispatch(new StockSyncFinished('foo'));
        Queue::assertNotPushed(SalesforceStockExportJob::class);
    }

    public function testOutboundUpdateEnabled()
    {
        Queue::fake();

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'DMS vehicle sync',
            'group' => 'SalesForce',
            'name' => 'Export Stock',
            'config_key' => 'sales-force-stock-export-enabled',
            'type' => 'boolean',
            'value' => true,
        ]);

        Event::dispatch(new StockSyncFinished('foo'));
        Queue::assertPushed(SalesforceStockExportJob::class);
    }

    public function testOutboundUpdateConfig()
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Sales Channels / Feeds',
            'group' => 'Salesforce',
            'name' => 'Export Stock to Salesforce',
            'config_key' => 'sales-force-stock-export-enabled',
            'type' => 'boolean',
            'value' => true,
            'description' => 'Send Autonomy site vehicle URL to SalesForce',
            'order' => 0,
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Sales Channels / Feeds',
            'group' => 'Salesforce',
            'name' => 'Username',
            'config_key' => 'sales-force-stock-export-username',
            'type' => 'string',
            'value' => 'test_username',
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Sales Channels / Feeds',
            'group' => 'Salesforce',
            'name' => 'Password',
            'config_key' => 'sales-force-stock-export-password',
            'type' => 'secret',
            'value' => 'test_password',
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Sales Channels / Feeds',
            'group' => 'Salesforce',
            'name' => 'Client ID',
            'config_key' => 'sales-force-stock-export-client-id',
            'type' => 'string',
            'value' => 'test_client_id',
        ]);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Sales Channels / Feeds',
            'group' => 'Salesforce',
            'name' => 'Client Secret',
            'config_key' => 'sales-force-stock-export-client-secret',
            'type' => 'secret',
            'value' => 'test_client_secret',
        ]);


        Http::fake([
            '*/token' => Http::response([
                'access_token' => 'ABC123'
            ], 200),
            '*/sobjects/*' => Http::response([
                'data' => 'example response data'
            ], 200),
            '*/services/data/v61.0/ui-api/records/batch' => Http::response([
                'data' => 'example response data'
            ], 200),
            '*' => Http::response(null, 200),
        ]);

        Vehicle::factory()->create([
            'uuid' => 'foo',
        ]);

        new SalesforceStockExportJob()->handle();

        Http::assertSent(function (Request $request) {
            if (!Str::contains($request->url(), 'token')) {
                return false;
            }

            return Str::contains($request->url(), 'token')
                && $request->data()['username'] == 'test_username'
                && $request->data()['password'] == 'test_password'
                && $request->data()['client_id'] == 'test_client_id'
                && $request->data()['client_secret'] == 'test_client_secret';
        });
    }

    public function testOutboundUpdateCallBatchSize()
    {
        Http::fake([
            '*/token' => Http::response([
                'access_token' => 'ABC123'
            ], 200),
            '*/sobjects/*' => Http::response([
                'data' => 'example response data'
            ], 200),
            '*/services/data/v61.0/ui-api/records/batch' => Http::response([
                'data' => 'example response data'
            ], 200),
            '*' => Http::response(null, 200),
        ]);

        $this->seed(SettingsDefinitionSeeder::class);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'General',
            'group' => 'Vehicles',
            'name' => 'Vehicle page URL path',
            'config_key' => 'vehicles-url-path',
            'type' => 'string',
            'value' => '/vehicle/{{SLUG}}',
        ]);

        Settings::update('automotive-vehicles-url_building_format', 'vrm-make-model');

        Vehicle::factory(5)->create();

        new SalesforceStockExportJob()->handle();

        Http::assertSent(function (Request $request) {
            if (Str::contains($request->url(), 'token')) {
                return false;
            }

            return Str::contains($request->url(), 'services/data/v61.0/ui-api/records/batch')
                && count($request->data()['operations'][0]['records']) == 5;
        });
    }

    public function testOutboundUpdateCallBatchContent()
    {
        Http::fake([
            '*/token' => Http::response([
                'access_token' => 'ABC123'
            ], 200),
            '*/sobjects/*' => Http::response([
                'data' => 'example response data'
            ], 200),
            '*/services/data/v61.0/ui-api/records/batch' => Http::response([
                'data' => 'example response data'
            ], 200),
            '*' => Http::response(null, 200),
        ]);

        $this->seed(SettingsDefinitionSeeder::class);

        Settings::make([
            'tab' => 'Automotive',
            'section' => 'General',
            'group' => 'Vehicles',
            'name' => 'Vehicle page URL path',
            'config_key' => 'vehicles-url-path',
            'type' => 'string',
            'value' => '/vehicle/{{SLUG}}',
        ]);

        Settings::update('automotive-vehicles-url_building_format', 'vrm-make-model');

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

        new SalesforceStockExportJob()->handle();

        Http::assertSent(function (Request $request) use ($vehicle) {
            if (Str::contains($request->url(), 'token')) {
                return false;
            }

            return Str::contains($request->url(), 'services/data/v61.0/ui-api/records/batch')
                && count($request->data()['operations'][0]['records']) == 1
                && $request->data()['operations'][0]['records'][0]['fields']['Id'] == $vehicle->uuid
                && Str::contains($request->data()['operations'][0]['records'][0]['fields']['Website__c'], 'foo');
        });
    }
}
