<?php

namespace Tests\Tenant;

use App\Facades\Settings;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Str;
use Mtc\ContentManager\Models\Media;
use Mtc\MercuryDataModels\CatalogOffer;
use Mtc\MercuryDataModels\CatalogOfferRule;
use Tests\TenantTestCase;
use Tests\UserForTenant;

class CatalogOfferControllerTest extends TenantTestCase
{
    use UserForTenant;

    public function testIndex()
    {
        CatalogOffer::factory(5)->create();
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson('/api/catalog-offers');

        $response->assertStatus(200);
        $this->assertArrayHasKey('data', $response->json());
        $this->assertIsArray($response->json('data'));
        $this->assertCount(5, $response->json('data'));
        $this->assertArrayHasKey('title', $response->json('data.0'));
        $this->assertArrayHasKey('link', $response->json('data.0'));
        $this->assertArrayHasKey('id', $response->json('data.0'));
        $this->assertArrayHasKey('type', $response->json('data.0'));
    }

    public function testIndexFiltersByVehicleType()
    {
        CatalogOffer::factory(3)->forVehicles()->create();
        CatalogOffer::factory(2)->forNewCars()->create();

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson('/api/catalog-offers?type=vehicle');

        $response->assertStatus(200);
        $this->assertCount(3, $response->json('data'));

        foreach ($response->json('data') as $offer) {
            $this->assertEquals('vehicle', $offer['type']);
        }
    }

    public function testIndexFiltersByNewCarType()
    {
        CatalogOffer::factory(3)->forVehicles()->create();
        CatalogOffer::factory(2)->forNewCars()->create();

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson('/api/catalog-offers?type=new_car');

        $response->assertStatus(200);
        $this->assertCount(2, $response->json('data'));

        foreach ($response->json('data') as $offer) {
            $this->assertEquals('new_car', $offer['type']);
        }
    }

    public function testShow()
    {
        $offer = CatalogOffer::factory()->create(['name' => 'foo baz bar']);
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.catalog-offers.show', $offer));

        $response->assertStatus(200);
        $this->assertIsArray($response->json());
        $this->assertEquals('foo baz bar', $response->json('name'));
        $this->assertArrayHasKey('media', $response->json());
        $this->assertArrayHasKey('subtitle', $response->json());
        $this->assertArrayHasKey('link', $response->json());
        $this->assertArrayHasKey('button_text', $response->json());
        $this->assertArrayHasKey('active', $response->json());
    }
    public function testStore()
    {
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.catalog-offers.store'), [
                'name' => 'foo baz bar'
            ]);

        $response->assertStatus(201);
        $this->assertEquals('foo baz bar', $response->json('name'));
        $this->assertEquals(0, $response->json('active'));
        $this->assertEquals('vehicle', $response->json('type'));
    }

    public function testStoreWithNewCarType()
    {
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.catalog-offers.store'), [
                'name' => 'New Car Offer',
                'type' => 'new_car'
            ]);

        $response->assertStatus(201);
        $this->assertEquals('New Car Offer', $response->json('name'));
        $this->assertEquals('new_car', $response->json('type'));
    }

    public function testUpdate()
    {
        Queue::fake();
        $media = Media::factory()->create();
        $offer = CatalogOffer::factory()->create(['name' => 'foo baz bar']);
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.catalog-offers.update', $offer), [
                'name' => 'lorem ipsum',
                'active' => true,
                'media' => [$media->id],
                'subtitle' => 'foo faz',
                'link' => '/foo-faz',
            ]);

        $response->assertStatus(200);
        $this->assertIsArray($response->json());
        $this->assertEquals('lorem ipsum', $response->json('name'));
        $offer->refresh();
        $this->assertTrue((bool)$offer->active);
        $this->assertEquals('foo faz', $offer->subtitle);
        $this->assertEquals('/foo-faz', $offer->link);
        $this->assertEquals(1, $offer->mediaUses()->count());
    }

    public function testDestroy()
    {
        $offer = CatalogOffer::factory()->create(['name' => 'foo baz bar']);
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->deleteJson(route('tenant.catalog-offers.destroy', $offer));

        $response->assertStatus(200);
        $this->assertFalse(CatalogOffer::query()->where('id', $offer->id)->exists());
    }

    public function testShowIncludesRules()
    {
        $offer = CatalogOffer::factory()->create(['name' => 'foo baz bar']);
        CatalogOfferRule::query()->create([
            'catalog_offer_id' => $offer->id,
            'field' => 'make_id',
            'condition' => '=',
            'value' => '123',
        ]);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->getJson(route('tenant.catalog-offers.show', $offer));

        $response->assertStatus(200);
        $this->assertArrayHasKey('rules', $response->json());
        $this->assertCount(1, $response->json('rules'));
        $this->assertEquals('make_id', $response->json('rules.0.field'));
        $this->assertEquals('=', $response->json('rules.0.condition'));
        $this->assertEquals('123', $response->json('rules.0.value'));
    }

    public function testUpdateWithRules()
    {
        Queue::fake();
        $offer = CatalogOffer::factory()->create(['name' => 'foo baz bar']);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.catalog-offers.update', $offer), [
                'name' => 'lorem ipsum',
                'active' => true,
                'subtitle' => 'foo faz',
                'link' => '/foo-faz',
                'rules' => [
                    [
                        'id' => Str::random(12),
                        'field' => 'make_id',
                        'condition' => '=',
                        'value' => '456',
                    ],
                    [
                        'id' => Str::random(12),
                        'field' => 'price',
                        'condition' => '>',
                        'value' => '10000',
                    ],
                ]
            ]);

        $response->assertStatus(200);
        $offer->refresh();
        $this->assertEquals(2, $offer->rules()->count());

        $rules = $offer->rules()->get();
        $this->assertEquals('make_id', $rules[0]->field);
        $this->assertEquals('=', $rules[0]->condition);
        $this->assertEquals('456', $rules[0]->value);
        $this->assertEquals('price', $rules[1]->field);
        $this->assertEquals('>', $rules[1]->condition);
        $this->assertEquals('10000', $rules[1]->value);
    }

    public function testUpdateRemovesDeletedRules()
    {
        Queue::fake();
        $offer = CatalogOffer::factory()->create(['name' => 'foo baz bar']);

        // Create initial rules
        $rule1 = CatalogOfferRule::query()->create([
            'catalog_offer_id' => $offer->id,
            'field' => 'make_id',
            'condition' => '=',
            'value' => '123',
        ]);
        $rule2 = CatalogOfferRule::query()->create([
            'catalog_offer_id' => $offer->id,
            'field' => 'price',
            'condition' => '>',
            'value' => '5000',
        ]);

        $this->assertEquals(2, $offer->rules()->count());

        // Update with only one rule (removing rule2)
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.catalog-offers.update', $offer), [
                'name' => 'lorem ipsum',
                'active' => true,
                'subtitle' => 'foo faz',
                'link' => '/foo-faz',
                'rules' => [
                    [
                        'id' => $rule1->id,
                        'field' => 'make_id',
                        'condition' => '!=',
                        'value' => '789',
                    ],
                ]
            ]);

        $response->assertStatus(200);
        $offer->refresh();
        $this->assertEquals(1, $offer->rules()->count());

        $rule = $offer->rules()->first();
        $this->assertEquals('make_id', $rule->field);
        $this->assertEquals('!=', $rule->condition);
        $this->assertEquals('789', $rule->value);

        // Verify rule2 was deleted
        $this->assertFalse(CatalogOfferRule::query()->where('id', $rule2->id)->exists());
    }

    public function testUpdateWithRuleArrayValueIsJsonEncoded()
    {
        Queue::fake();
        $offer = CatalogOffer::factory()->create(['name' => 'foo baz bar']);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.catalog-offers.update', $offer), [
                'name' => 'lorem ipsum',
                'active' => true,
                'subtitle' => 'foo faz',
                'link' => '/foo-faz',
                'rules' => [
                    [
                        'id' => Str::random(12),
                        'field' => 'dealership_id',
                        'condition' => 'in',
                        'value' => [2, 5, 9, 10],
                    ],
                ]
            ]);

        $response->assertStatus(200);
        $offer->refresh();
        $this->assertEquals(1, $offer->rules()->count());

        $rule = $offer->rules()->first();
        $this->assertEquals('dealership_id', $rule->field);
        $this->assertEquals('in', $rule->condition);
        $this->assertEquals('[2,5,9,10]', $rule->value);
    }

    public function testWeightSavedWhenFeatureOff(): void
    {
        // catalog-offer-max-weighted defaults to 0 (feature off) — weight saves but no cap enforced
        $existingWeighted = CatalogOffer::factory()->create(['weight' => 5]);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.catalog-offers.store'), [
                'name' => 'New Weighted Offer',
                'weight' => 1,
            ]);

        $response->assertStatus(201);
        $this->assertEquals(1, $response->json('weight'));

        // Existing weighted offer untouched since feature is off
        $this->assertEquals(5, $existingWeighted->fresh()->weight);
    }

    public function testStoreWeightedOfferWithinCap(): void
    {
        $this->seedMaxWeightedSetting(2);

        $existingWeighted = CatalogOffer::factory()->create(['weight' => 1]);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.catalog-offers.store'), [
                'name' => 'Second Weighted Offer',
                'weight' => 1,
            ]);

        $response->assertStatus(201);
        $this->assertEquals(1, $response->json('weight'));

        // Existing offer still weighted — cap not exceeded
        $this->assertEquals(1, $existingWeighted->fresh()->weight);
    }

    public function testStoreWeightedOfferDemotesLowestWhenCapExceeded(): void
    {
        $this->seedMaxWeightedSetting(2);

        $lowWeighted = CatalogOffer::factory()->create(['weight' => 1]);
        $highWeighted = CatalogOffer::factory()->create(['weight' => 3]);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->postJson(route('tenant.catalog-offers.store'), [
                'name' => 'Third Weighted Offer',
                'weight' => 2,
            ]);

        $response->assertStatus(201);

        // Lowest weight (1) demoted to 0 to make room
        $this->assertEquals(0, $lowWeighted->fresh()->weight);
        // Higher weight untouched
        $this->assertEquals(3, $highWeighted->fresh()->weight);
    }

    public function testUpdateWeightedOfferDemotesLowestWhenCapExceeded(): void
    {
        Queue::fake();
        $this->seedMaxWeightedSetting(2);

        $lowWeighted = CatalogOffer::factory()->create(['weight' => 1]);
        $highWeighted = CatalogOffer::factory()->create(['weight' => 3]);
        $offer = CatalogOffer::factory()->create(['weight' => 0]);

        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.catalog-offers.update', $offer), [
                'name' => $offer->name,
                'subtitle' => '',
                'link' => '/foo',
                'weight' => 2,
            ]);

        $response->assertStatus(200);
        $this->assertEquals(2, $offer->fresh()->weight);

        // Lowest weight (1) demoted to 0 to make room
        $this->assertEquals(0, $lowWeighted->fresh()->weight);
        // Higher weight untouched
        $this->assertEquals(3, $highWeighted->fresh()->weight);
    }

    private function seedMaxWeightedSetting(int $value): void
    {
        Settings::make([
            'tab' => 'Automotive',
            'section' => 'Filter',
            'group' => 'General',
            'name' => 'Max weighted offer cards on page 1',
            'config_key' => 'catalog-offer-max-weighted',
            'type' => 'integer',
            'value' => $value,
        ]);
    }

    public function testUpdateWithEmptyRulesRemovesAllRules()
    {
        Queue::fake();
        $offer = CatalogOffer::factory()->create(['name' => 'foo baz bar']);

        // Create initial rules
        CatalogOfferRule::query()->create([
            'catalog_offer_id' => $offer->id,
            'field' => 'make_id',
            'condition' => '=',
            'value' => '123',
        ]);

        $this->assertEquals(1, $offer->rules()->count());

        // Update with empty rules array
        $response = $this->actingAs($this->getUser())
            ->withHeader('X-Tenant', tenant('id'))
            ->putJson(route('tenant.catalog-offers.update', $offer), [
                'name' => 'lorem ipsum',
                'active' => true,
                'subtitle' => 'foo faz',
                'link' => '/foo-faz',
                'rules' => []
            ]);

        $response->assertStatus(200);
        $offer->refresh();
        $this->assertEquals(0, $offer->rules()->count());
    }
}
