<?php

namespace Tests\Feature;

use App\Domain\Financial\Models\Invoice;
use App\Domain\Participant\Models\Participant;
use App\Models\User;
use Tests\TestCase;

/**
 * The settlement screens are Blade, so a renamed method or a wrong variable is
 * a 500 at request time and nothing earlier catches it.
 *
 * Runs against a restored Postgres tenant; skips on the default SQLite suite:
 *
 *   DB_CONNECTION=pgsql DB_DATABASE=oc_sport_test ./vendor/bin/phpunit --filter SettlementScreensRenderTest
 */
class SettlementScreensRenderTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();

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

    private function anAdmin(): User
    {
        $user = User::query()->get()->first(fn (User $u) => $u->can('settlements.manage'));

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

        return $user;
    }

    public function test_the_worklist_renders_and_finds_accounts(): void
    {
        $response = $this->actingAs($this->anAdmin())->get(route('admin.settlement-worklist'));

        $response->assertOk();
        $response->assertSee('حالات تحتاج تسوية', escape: false);
        $response->assertSee('تسوية', escape: false);
    }

    public function test_the_worklist_can_be_filtered_to_one_case(): void
    {
        $response = $this->actingAs($this->anAdmin())
            ->get(route('admin.settlement-worklist', ['case' => 'never_paid']));

        $response->assertOk();
        $response->assertSee('لم يُسجَّل له أي دفع', escape: false);
    }

    public function test_the_wizard_opens_on_a_participant_and_shows_the_account(): void
    {
        // A live participant: an invoice whose player was later deleted must
        // not open the wizard, and the screen says so rather than pretending.
        $invoice = Invoice::withoutGlobalScopes()
            ->where('billable_type', Participant::class)
            ->whereColumn('total_amount', '>', 'paid_amount')
            ->whereNull('deleted_at')
            ->whereIn('billable_id', Participant::withoutGlobalScopes()->whereNull('deleted_at')->select('id'))
            ->firstOrFail();

        $response = $this->actingAs($this->anAdmin())
            ->get(route('admin.account-settlement', ['participant' => $invoice->billable_id]));

        $response->assertOk();
        $response->assertSee('الفواتير', escape: false);
        $response->assertSee('دفعة سابقة', escape: false);
        $response->assertSee($invoice->number);
    }

    public function test_the_wizard_opens_empty_without_a_participant(): void
    {
        $response = $this->actingAs($this->anAdmin())->get(route('admin.account-settlement'));

        $response->assertOk();
        $response->assertSee('ابحث بالاسم أو رقم الهاتف', escape: false);
    }

    public function test_a_user_without_the_permission_is_refused(): void
    {
        $user = User::query()->get()->first(fn (User $u) => ! $u->can('settlements.manage'));

        if (! $user) {
            $this->markTestSkipped('Every user in this tenant may settle accounts.');
        }

        $this->actingAs($user)->get(route('admin.settlement-worklist'))->assertForbidden();
        $this->actingAs($user)->get(route('admin.account-settlement'))->assertForbidden();
    }

    public function test_the_screens_use_logical_properties_only(): void
    {
        // Arabic is the default locale; a physical margin flips the layout.
        foreach ([
            'livewire/admin/account-settlement-wizard.blade.php',
            'livewire/admin/settlement-worklist.blade.php',
            'livewire/admin/partials/settlement-cart.blade.php',
        ] as $view) {
            $content = file_get_contents(resource_path('views/' . $view));

            $offenders = [];
            foreach (explode("\n", $content) as $i => $line) {
                if (preg_match('/class="[^"]*\b(m[lr]|p[lr])-[0-9]/', $line)) {
                    $offenders[] = $view . ':' . ($i + 1) . ' ' . trim($line);
                }
            }

            $this->assertSame([], $offenders, "Physical margins/paddings found:\n" . implode("\n", $offenders));
        }
    }

    public function test_no_dead_links_in_the_settlement_screens(): void
    {
        // href="#" is forbidden: a link that goes nowhere is a bug report from
        // a parent standing at the desk.
        foreach ([
            'livewire/admin/account-settlement-wizard.blade.php',
            'livewire/admin/settlement-worklist.blade.php',
            'livewire/admin/partials/settlement-cart.blade.php',
        ] as $view) {
            $content = file_get_contents(resource_path('views/' . $view));
            $this->assertStringNotContainsString('href="#"', $content, $view);
        }
    }
}
