<?php

namespace Tests\Feature;

use App\Domain\Identity\Models\Branch;
use App\Domain\Identity\Services\BranchSettingsService;
use App\Domain\Pricing\Enums\PricingRuleType;
use App\Domain\Pricing\Support\ConditionSchema;
use App\Models\User;
use Livewire\Livewire;
use Tests\TestCase;

/**
 * The screens where a discount is authored and a branch is configured.
 *
 * Both grew a new control that nothing else renders: the member/non-member
 * condition on the discount builder, and the "this branch bills nothing"
 * toggle on the branch form. A rule type whose condition inputs no view knows
 * how to draw is a blank step the desk cannot get past, and neither the wizard
 * nor the branch form was covered by any render test.
 *
 * Runs against a restored Postgres tenant; skips on the default SQLite suite:
 *
 *   DB_CONNECTION=pgsql DB_DATABASE=oc_sport_test ./vendor/bin/phpunit --filter DiscountAndBranchScreensRenderTest
 */
class DiscountAndBranchScreensRenderTest extends TestCase
{
    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);
    }

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

    public function test_the_discount_wizard_renders(): void
    {
        $this->actingAs($this->anOwner())
            ->get(route('pricing.rules.wizard'))
            ->assertOk();
    }

    public function test_the_membership_tier_condition_draws_its_own_choices(): void
    {
        // The list-kind partial falls through to a branch picker for any rule
        // type it does not recognise, so an unrecognised one renders a step
        // offering branches where it should offer members.
        Livewire::actingAs($this->anOwner())
            ->test(\App\Livewire\Pricing\CreatePricingRuleWizard::class)
            ->call('startBlank')
            ->set('ruleType', PricingRuleType::MembershipType->value)
            ->assertOk()
            ->assertSee(ConditionSchema::membershipTypeLabel('member'), escape: false)
            ->assertSee(ConditionSchema::membershipTypeLabel('non_member'), escape: false);
    }

    public function test_a_members_only_discount_saves_with_its_condition_intact(): void
    {
        $branchId = Branch::query()->value('id');

        $component = Livewire::actingAs($this->anOwner())
            ->test(\App\Livewire\Pricing\CreatePricingRuleWizard::class)
            ->call('startBlank')
            ->set('ruleType', PricingRuleType::MembershipType->value)
            ->set('nameAr', 'خصم اختبار للأعضاء')
            ->set('adjustmentType', 'percentage_discount')
            ->set('amount', '10')
            ->set('conditions', ['values' => ['member']])
            ->set('branchIds', $branchId ? [$branchId] : [])
            ->set('effectiveFrom', now()->toDateString())
            ->call('save')
            ->assertHasNoErrors();

        $rule = \App\Domain\Pricing\Models\PricingRule::withoutGlobalScopes()
            ->where('name_ar', 'خصم اختبار للأعضاء')
            ->latest('id')
            ->first();

        $this->assertNotNull($rule, 'The wizard reported success but wrote nothing.');
        $this->assertSame(['values' => ['member']], $rule->conditions, 'ConditionSchema::normalize() dropped the condition.');

        if ($branchId) {
            // pricing_rule_branches.academy_id is NOT NULL and the pivot has no
            // model to stamp it, so a bare sync() threw and no branch-targeted
            // discount could be saved at all.
            $this->assertSame(
                [$branchId],
                $rule->branches()->pluck('branches.id')->all(),
                'The rule saved without the branch it was targeted at.'
            );
            $this->assertNotNull(
                \Illuminate\Support\Facades\DB::table('pricing_rule_branches')
                    ->where('pricing_rule_id', $rule->id)
                    ->value('academy_id'),
                'The pivot row carries no academy.'
            );
        }

        $rule->branches()->detach();
        $rule->forceDelete();

        unset($component);
    }

    public function test_a_tier_the_engine_never_heard_of_is_refused(): void
    {
        Livewire::actingAs($this->anOwner())
            ->test(\App\Livewire\Pricing\CreatePricingRuleWizard::class)
            ->call('startBlank')
            ->set('ruleType', PricingRuleType::MembershipType->value)
            ->set('nameAr', 'خصم اختبار مرفوض')
            ->set('amount', '10')
            ->set('conditions', ['values' => ['vip']])
            ->set('effectiveFrom', now()->toDateString())
            ->call('save')
            ->assertHasErrors('conditions.values.0');
    }

    public function test_the_branch_form_renders_and_offers_the_billing_toggle(): void
    {
        $this->actingAs($this->anOwner())
            ->get(route('branches.create'))
            ->assertOk()
            ->assertSee('الإيرادات بتتحصل خارج النظام', escape: false);
    }

    public function test_the_billing_question_can_be_asked_from_the_console(): void
    {
        $branch = Branch::query()->first();

        $this->assertNotNull($branch, 'The restored tenant has no branches.');

        // GenerateRenewalInvoices asks this per enrolment, from a scheduled
        // command that binds no academy. Reading it through the academy
        // fallback threw "Target class [current_academy] does not exist" and
        // would have taken the nightly billing run down with it.
        app()->forgetInstance('current_academy');

        $this->assertIsBool(app(BranchSettingsService::class)->billingHandledExternally($branch->id));
    }

    public function test_the_billing_toggle_round_trips_through_branch_settings(): void
    {
        $branch = Branch::query()->first();

        $this->assertNotNull($branch, 'The restored tenant has no branches.');

        $settings = app(BranchSettingsService::class);
        $before = $settings->billingHandledExternally($branch->id);

        try {
            $settings->set($branch->id, BranchSettingsService::KEY_BILLING_EXTERNAL, '1', 'financial');

            // A fresh instance, because the service memoises per request.
            $this->assertTrue(app(BranchSettingsService::class)->billingHandledExternally($branch->id));

            $settings->set($branch->id, BranchSettingsService::KEY_BILLING_EXTERNAL, '0', 'financial');
            $this->assertFalse(app(BranchSettingsService::class)->billingHandledExternally($branch->id));
        } finally {
            $settings->set(
                $branch->id,
                BranchSettingsService::KEY_BILLING_EXTERNAL,
                $before ? '1' : '0',
                'financial'
            );
        }
    }
}
