<?php

namespace Tests\Feature;

use App\Console\Commands\SyncVehicleImages;
use App\Enums\AutosOnShowNotificationType;
use App\Facades\Settings;
use App\ImageSyncServiceHelper;
use App\Jobs\ImportAutosOnShowFilesJob;
use App\MediaRepository;
use App\Services\AutosOnShow;
use App\Tier;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;
use Mockery\MockInterface;
use Mtc\MercuryDataModels\ApiNotification;
use Mtc\MercuryDataModels\Media;
use Mtc\MercuryDataModels\MediaUse;
use Mtc\MercuryDataModels\Vehicle;
use Tests\TestCase;

class AutosOnShowTest extends TestCase
{
    use RefreshDatabase;
    use WithFaker;

    protected $tenancy = true;

    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function testNotificationEndpoint()
    {
        Queue::fake();
        Config::set('services.image-sync.autos-on-show.notification_token', 'abc');
        $vehicle = Vehicle::factory()->create();
        $response = $this->postJson(route('tenant.notifications.autos-on-show', tenant('id')), [
            'api_token' => 'abc',
            'vehicleId' => $vehicle->registration_number,
        ], [
            'Authorization' => 'Bearer abc'
        ]);

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

    public function testTriggerFetchImages()
    {
        tenant()->update(['tier' => Tier::STANDARD->value]);
        Settings::make([
            'tab' => 'automotive',
            'section' => 'image-sync',
            'group' => 'autos-on-show',
            'name' => 'enabled',
            'config_key' => 'image-sync-autos-on-show-enabled',
            'value' => true,
            'type' => 'boolean',
        ]);
        Storage::fake('media');
        if (empty($this->faker->image)) {
            $this->markTestSkipped('Unable to generate test image');
        }
        Http::fake([
            'https://api.aos.tv/v2/vehicles/*' => $this->response(),
            'https://uat-api.aos.tv/v2/vehicles' => $this->response(),
            'https://api.aos.tv/v2/token' => Http::response(['access_token' => 'foo-baz']),
            'https://uat-api.aos.tv/v2/token' => Http::response(['access_token' => 'foo-baz']),
            '*' => Http::response('Wrong Request', 400),
        ]);
        $vehicle = Vehicle::factory()->create();
        (new ImageSyncServiceHelper())->triggerSync($vehicle);

        $vehicle->refresh();
        $this->assertEquals('http://foo.baz/bar', $vehicle->exterior_video_url);
        $this->assertEquals(1, $vehicle->mediaUses()->count());
    }

    public function testVehicleMissing()
    {
        Config::set('services.image-sync.autos-on-show.notification_token', 'abc');
        $response = $this->postJson(route('tenant.notifications.autos-on-show', tenant('id')), [
            'vehicleId' => 1
        ], [
            'Authorization' => 'Bearer abc'
        ]);

        // 422 disabled to allow logging and deferred processing
        $response->assertStatus(200);
    }

    public function testNoAuthKey()
    {
        Config::set('services.image-sync.autos-on-show.notification_token', 'abc');
        $response = $this->postJson(route('tenant.notifications.autos-on-show', tenant('id')));

        $response->assertStatus(403);
    }

    public function testStockImageSyncQueuesJob()
    {
        Settings::make([
            'tab' => 'automotive',
            'section' => 'image-sync',
            'group' => 'autos-on-show',
            'name' => 'enabled',
            'config_key' => 'image-sync-autos-on-show-enabled',
            'value' => true,
            'type' => 'boolean',
        ]);

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

        ApiNotification::query()->create([
            'processed' => null,
            'provider' => \App\Modules\ImageSync\AutosOnShow::PROVIDER_NAME,
            'reference' => $vehicle->vrm_condensed,
            'data' => [
                'images' => [
                    'a.png',
                    'b.png',
                    'c.png',
                ],
                'notificationTypeId' => AutosOnShowNotificationType::VEHICLE_REIMAGED->value
            ],
        ]);

        Queue::fake();
        $command = $this->artisan(SyncVehicleImages::class);
        $command->run();

        Queue::assertPushed(ImportAutosOnShowFilesJob::class);
    }

    public function testFileImportJob()
    {
        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_id')
            ->andReturn('client-id');
        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_secret')
            ->andReturn('client-secret');
        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_id_test')
            ->andReturn('');
        Settings::shouldReceive('get')
            ->withAnyArgs()
            ->andReturn(null);

        Storage::fake('media');

        Http::fake([
            'https://api.aos.tv/v2/vehicles/*' => $this->response(),
            'https://uat-api.aos.tv/v2/vehicles' => $this->response(),
            'https://uat-api.aos.tv/v2/vehicles/*' => $this->response(),
            'https://api.aos.tv/v2/token' => Http::response(['access_token' => 'foo-baz']),
            'https://uat-api.aos.tv/v2/token' => Http::response(['access_token' => 'foo-baz']),
            '*' => Http::response('Wrong Request', 400),
        ]);

        Vehicle::unsetEventDispatcher();
        $vehicle = Vehicle::factory()->create([
            'is_published' => true,
        ]);

        $media = Media::factory()->create([
            'upload_date' => now()->format('Y-m'),
        ]);

        \Mtc\ContentManager\Facades\Media::shouldReceive('importImageFromUrl')
            ->once()
            ->andReturn($media);

        \Mtc\ContentManager\Facades\Media::shouldReceive('setUsesForModel')
            ->once()
            ->withArgs(function ($mediaIds, $model) use ($media) {
                \Mtc\ContentManager\Models\MediaUse::create([
                    'media_id' => $media->id,
                    'owner_type' => $model->getMorphClass(),
                    'owner_id' => $model->id,
                ]);
                return true;
            })
            ->andReturnNull();

        (new ImportAutosOnShowFilesJob($vehicle, app(AutosOnShow::class)))->handle();

        $this->assertCount(
            1,
            MediaUse::query()
                ->where('owner_type', 'vehicle')
                ->where('owner_id', $vehicle->id)
                ->get()
        );
    }

    public function testGetVehicleMedia()
    {
        Http::fake([
            'https://api.aos.tv/v2/vehicles/AB12CDE' => Http::response(['images' => []]),
            'https://api.aos.tv/v2/token' => Http::response(['access_token' => 'abc123']),
        ]);

        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_id')
            ->andReturn('client-id');
        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_secret')
            ->andReturn('client-secret');
        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_id_test')
            ->andReturn('');

        $service = new AutosOnShow();
        $response = $service->getVehicleMedia('AB12 CDE');

        $this->assertIsArray($response);
        $this->assertArrayHasKey('images', $response);
    }

    public function testAuthTokenCanBeCached()
    {
        Http::fake([
            'https://uat-api.aos.tv/v2/token' => Http::response(['access_token' => 'cached-token']),
            'https://api.aos.tv/v2/token' => Http::response(['access_token' => 'cached-token']),
        ]);

        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_id')
            ->andReturn('client-id');
        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_secret')
            ->andReturn('client-secret');
        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_id_test')
            ->andReturn('');

        $service = new AutosOnShow();
        $token1 = $this->invokeMethod($service, 'authToken');
        $token2 = $this->invokeMethod($service, 'authToken');

        $this->assertEquals('cached-token', $token1);
        $this->assertEquals('cached-token', $token2);
    }

    public function testEndpointForLiveAndUAT()
    {
        $service = new AutosOnShow();

        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_id')
            ->andReturn('client-id');
        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_secret')
            ->andReturn('client-secret');
        Settings::shouldReceive('get')
            ->with('image-sync-autos-on-show-client_id_test')
            ->andReturn('test-client-id', '');

        $endpoint = $this->invokeMethod($service, 'endpoint', ['vehicles/123']);
        $this->assertStringStartsWith('https://uat-api.aos.tv/v2/', $endpoint);

        $endpoint = $this->invokeMethod($service, 'endpoint', ['/vehicles/123']);
        $this->assertStringStartsWith('https://api.aos.tv/v2/', $endpoint);
    }

    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);
    }

    private function response()
    {
        return Http::response([
            'videourl' => 'http://foo.baz/bar',
            'images' => [
                ['large' => $this->faker->url]
            ],
        ]);
    }
}
