<?php

namespace Tests\Unit;

use App\Facades\Settings;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Mtc\ContentManager\Models\ContentElement;
use Mtc\ContentManager\Models\ContentElementField;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\VehicleModel;
use Mtc\MercuryDataModels\VehicleOffer;
use Mtc\MercuryDataModels\VehicleOfferContent;
use Tests\TenantTestCase;
use Tests\UserForTenant;

class VehicleOfferTest extends TenantTestCase
{
    use UserForTenant;

    public function testSearchContentSetWhenSaving()
    {
        $make = VehicleMake::factory()->create(['name' => 'bmw']);
        $model = VehicleModel::factory()->create(['name' => '330i']);
        $vehicle = VehicleOffer::factory()->create([
            'make_id' => $make->id,
            'model_id' => $model->id,
            'derivative' => 'e92 Petrol Coupe',
        ]);

        $element = ContentElement::factory()->create();
        $field = ContentElementField::factory()->create([
            'element_id' => $element->id,
            'field_type' => 'textarea',
        ]);

        VehicleOfferContent::factory()->create([
            'offer_id' => $vehicle->id,
            'parent_id' => null,
            'element_id' => $element->id,
            'content' => [
                $field->name => 'foo baz bar'
            ]
        ]);

        // Manually touch vehicle to update search content.
        // In controller this is done by updating offer after content
        $vehicle->touch();

        $this->assertStringContainsString('bmw', $vehicle->search_content);
        $this->assertStringContainsString('330i', $vehicle->search_content);
        $this->assertStringContainsString('e92', $vehicle->search_content);
        $this->assertStringContainsString('foo', $vehicle->search_content);
        $this->assertStringContainsString('baz', $vehicle->search_content);
    }

    public function testSaveOrderUpdatesCorrectly()
    {
        $offers = VehicleOffer::factory()
            ->count(3)
            ->sequence(
                ['order' => 1],
                ['order' => 2],
                ['order' => 3],
            )->create();

        $newOrder = [$offers[2]->id, $offers[0]->id, $offers[1]->id];
        $payload = collect($newOrder)->map(fn($id) => ['id' => $id])->toArray();

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.vehicle-offers.save-order'), $payload);

        $response->assertOk();

        $this->assertDatabaseHas('vehicle_offers', ['id' => $offers[2]->id, 'order' => 1]);
        $this->assertDatabaseHas('vehicle_offers', ['id' => $offers[0]->id, 'order' => 2]);
        $this->assertDatabaseHas('vehicle_offers', ['id' => $offers[1]->id, 'order' => 3]);
    }

    public function testSetOrderValueEnd()
    {
        Settings::shouldReceive('get')
            ->with('offers-new-offer-position')
            ->andReturn('end');

        Settings::shouldReceive('get')
            ->byDefault()
            ->andReturn(null);

        VehicleOffer::factory()
            ->count(2)
            ->sequence(
                ['order' => 0],
                ['order' => 1],
            )->create();

        $offer = VehicleOffer::factory()->make(['order' => null]);

        (new \App\Observers\VehicleOfferObserver())->saving($offer);

        $this->assertEquals(2, $offer->order);
    }

    public function testSetOrderValueBeginning()
    {
        Settings::shouldReceive('get')
            ->with('offers-new-offer-position')
            ->andReturn('beginning');

        Settings::shouldReceive('get')
            ->byDefault()
            ->andReturn(null);

        VehicleOffer::factory()
            ->count(2)
            ->sequence(
                ['order' => 0],
                ['order' => 1],
            )->create();

        $offer = VehicleOffer::factory()->make(['order' => null]);

        (new \App\Observers\VehicleOfferObserver())->saving($offer);

        $this->assertEquals(0, $offer->order);

        $this->assertEquals(1, VehicleOffer::find(1)->order);
        $this->assertEquals(2, VehicleOffer::find(2)->order);
    }

    public function testSaveOrderLogsOnException()
    {
        Log::shouldReceive('error')->once();

        DB::shouldReceive('transaction')->andThrow(new \Exception('DB error'));

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.vehicle-offers.save-order'), [['id' => 1]]);

        $response->assertStatus(500);
        $response->assertJson([
            'status' => 'error',
            'message' => 'Failed to save the new order. Please try again.',
        ]);
    }
}
