<?php

namespace Tests\Feature;

use App\Domain\Identity\Models\Branch;
use App\Domain\Inventory\Models\Product;
use App\Domain\Inventory\Models\ProductCustomization;
use App\Livewire\Inventory\ProductForm;
use App\Livewire\Receptionist\NewRegistrationWizard;
use App\Models\User;
use Livewire\Livewire;
use Tests\TestCase;

/**
 * A product that cannot be sold without a size, a colour or a printed name.
 *
 * The pressure this is under: the cart is a browser-held array, so "the
 * dropdown only offered these four sizes" is not a check. Both the step guard
 * and confirm() re-read the questions from the database and re-check every
 * answer, and these tests aim at exactly that seam.
 *
 * Runs against a restored Postgres tenant; skips on the default SQLite suite:
 *
 *   DB_CONNECTION=pgsql DB_DATABASE=oc_sport_test ./vendor/bin/phpunit --filter ProductCustomizationTest
 */
class ProductCustomizationTest extends TestCase
{
    private ?Product $product = null;

    protected function setUp(): void
    {
        parent::setUp();

        if (config('database.default') !== 'pgsql') {
            $this->markTestSkipped('Needs a restored Postgres tenant; see the class comment.');
        }

        $academy = \App\Domain\Shared\Models\Academy::query()->first();

        if (! $academy) {
            $this->markTestSkipped('No academy in the restored tenant.');
        }

        app()->instance('current_academy', $academy);
    }

    protected function tearDown(): void
    {
        if ($this->product) {
            ProductCustomization::withoutBranchScope()->where('product_id', $this->product->id)->forceDelete();
            Product::withoutBranchScope()->whereKey($this->product->id)->forceDelete();
            $this->product = null;
        }

        parent::tearDown();
    }

    private function anOwner(): User
    {
        $user = User::withoutGlobalScopes()
            ->whereHas('primaryRole', fn ($q) => $q->where('slug', 'academy_owner'))
            ->first();

        if (! $user) {
            $this->markTestSkipped('No academy_owner in the restored tenant.');
        }

        return $user;
    }

    /** A product with one required size question and one optional free-text one. */
    private function aCustomisedProduct(): Product
    {
        $academy = app('current_academy');
        $branch = Branch::withoutGlobalScopes()->where('academy_id', $academy->id)->first();

        if (! $branch) {
            $this->markTestSkipped('No branch in the restored tenant.');
        }

        $this->product = Product::create([
            'academy_id' => $academy->id,
            'branch_id' => $branch->id,
            'sku' => 'TEST-CZ-' . uniqid(),
            'name_ar' => 'قميص تدريب اختباري',
            'name' => 'Test training shirt',
            'type' => 'physical',
            'selling_price' => 25000,
            'track_inventory' => false,
            'is_active' => true,
            'is_essential' => true,
            'created_by' => $this->anOwner()->id,
        ]);

        ProductCustomization::create([
            'academy_id' => $academy->id,
            'product_id' => $this->product->id,
            'name_ar' => 'المقاس',
            'type' => 'select',
            'options' => ['سمول', 'ميديم', 'لارج'],
            'is_required' => true,
            'sort_order' => 0,
        ]);

        ProductCustomization::create([
            'academy_id' => $academy->id,
            'product_id' => $this->product->id,
            'name_ar' => 'الاسم المطبوع',
            'type' => 'text',
            'options' => [],
            'is_required' => false,
            'sort_order' => 1,
        ]);

        return $this->product->fresh();
    }

    // --- The admin side ---

    public function test_the_product_form_saves_a_list_customization_with_its_options(): void
    {
        Livewire::actingAs($this->anOwner())
            ->test(ProductForm::class)
            ->set('name_ar', 'منتج اختبار التخصيصات')
            ->set('selling_price', '100')
            ->set('type', 'physical')
            ->call('addCustomizationRow')
            ->set('customizationRows.0.name_ar', 'المقاس')
            ->set('customizationRows.0.type', 'select')
            ->set('customizationRows.0.options_text', 'سمول، ميديم، لارج')
            ->call('save')
            ->assertHasNoErrors();

        $this->product = Product::withoutBranchScope()
            ->where('name_ar', 'منتج اختبار التخصيصات')
            ->latest('id')
            ->first();

        $this->assertNotNull($this->product);

        $customization = ProductCustomization::withoutBranchScope()
            ->where('product_id', $this->product->id)
            ->first();

        $this->assertNotNull($customization);
        $this->assertSame('select', $customization->type->value);
        $this->assertSame(['سمول', 'ميديم', 'لارج'], $customization->allowedValues());
    }

    public function test_a_list_customization_with_no_options_is_refused(): void
    {
        // An empty dropdown is a sale nobody can complete — the desk would be
        // asked for an answer the product offers no way to give.
        Livewire::actingAs($this->anOwner())
            ->test(ProductForm::class)
            ->set('name_ar', 'منتج بخيارات فارغة')
            ->set('selling_price', '100')
            ->call('addCustomizationRow')
            ->set('customizationRows.0.name_ar', 'المقاس')
            ->set('customizationRows.0.type', 'select')
            ->set('customizationRows.0.options_text', '  ،  ، ')
            ->call('save')
            ->assertHasErrors('customizationRows.0.options_text');
    }

    // --- The desk side ---

    public function test_the_cart_carries_the_questions_the_product_asks(): void
    {
        $product = $this->aCustomisedProduct();

        $component = Livewire::actingAs($this->anOwner())
            ->test(NewRegistrationWizard::class)
            ->call('addHotbuyItem', $product->id, 'product');

        $cart = $component->get('hotbuyCart');
        $key = "product_{$product->id}";

        $this->assertArrayHasKey($key, $cart);
        $this->assertCount(2, $cart[$key]['customizations']);
        $this->assertSame('المقاس', $cart[$key]['customizations'][0]['name_ar']);
        $this->assertSame(['سمول', 'ميديم', 'لارج'], $cart[$key]['customizations'][0]['options']);
    }

    public function test_the_cart_draws_the_questions_and_flags_the_unanswered_ones(): void
    {
        // The blade reads $this->customizationGaps per cart row; a fumbled key
        // there throws at render time, not at guard time.
        Livewire::actingAs($this->anOwner())
            ->test(NewRegistrationWizard::class)
            ->call('addHotbuyItem', $this->aCustomisedProduct()->id, 'product')
            ->set('currentStep', 5)
            ->assertOk()
            ->assertSee('بيانات المنتج المطلوبة', escape: false)
            ->assertSee('المقاس', escape: false)
            ->assertSee('ميديم', escape: false)
            ->assertSee('غير مكتملة', escape: false);
    }

    public function test_a_blank_required_customization_blocks_the_step_before_payment(): void
    {
        $product = $this->aCustomisedProduct();

        Livewire::actingAs($this->anOwner())
            ->test(NewRegistrationWizard::class)
            ->call('addHotbuyItem', $product->id, 'product')
            ->set('currentStep', 5)
            ->call('nextStep')
            ->assertHasErrors('hotbuyCustomizations')
            ->assertSet('currentStep', 5);
    }

    public function test_an_answered_required_customization_lets_the_step_through(): void
    {
        $product = $this->aCustomisedProduct();
        $sizeId = $product->activeCustomizations->firstWhere('name_ar', 'المقاس')->id;

        Livewire::actingAs($this->anOwner())
            ->test(NewRegistrationWizard::class)
            ->call('addHotbuyItem', $product->id, 'product')
            ->set('currentStep', 5)
            ->set("hotbuyCustomizations.product_{$product->id}.{$sizeId}", 'ميديم')
            ->call('nextStep')
            ->assertHasNoErrors()
            ->assertSet('currentStep', 6);
    }

    public function test_an_option_the_product_never_offered_is_refused(): void
    {
        // hotbuyCustomizations is browser-settable by design. The dropdown is a
        // convenience; the check is that the value is one of the product's own.
        $product = $this->aCustomisedProduct();
        $sizeId = $product->activeCustomizations->firstWhere('name_ar', 'المقاس')->id;

        Livewire::actingAs($this->anOwner())
            ->test(NewRegistrationWizard::class)
            ->call('addHotbuyItem', $product->id, 'product')
            ->set('currentStep', 5)
            ->set("hotbuyCustomizations.product_{$product->id}.{$sizeId}", 'إكس لارج مجاناً')
            ->call('nextStep')
            ->assertHasErrors('hotbuyCustomizations')
            ->assertSet('currentStep', 5);
    }

    public function test_an_optional_customization_left_blank_does_not_block(): void
    {
        $product = $this->aCustomisedProduct();
        $sizeId = $product->activeCustomizations->firstWhere('name_ar', 'المقاس')->id;

        Livewire::actingAs($this->anOwner())
            ->test(NewRegistrationWizard::class)
            ->call('addHotbuyItem', $product->id, 'product')
            ->set('currentStep', 5)
            ->set("hotbuyCustomizations.product_{$product->id}.{$sizeId}", 'لارج')
            // 'الاسم المطبوع' deliberately left empty
            ->call('nextStep')
            ->assertSet('currentStep', 6);
    }

    public function test_removing_the_item_drops_its_answers(): void
    {
        $product = $this->aCustomisedProduct();
        $sizeId = $product->activeCustomizations->firstWhere('name_ar', 'المقاس')->id;
        $key = "product_{$product->id}";

        $component = Livewire::actingAs($this->anOwner())
            ->test(NewRegistrationWizard::class)
            ->call('addHotbuyItem', $product->id, 'product')
            ->set("hotbuyCustomizations.{$key}.{$sizeId}", 'لارج')
            ->call('removeHotbuyItem', $key);

        $this->assertArrayNotHasKey($key, $component->get('hotbuyCustomizations'));
    }
}
