<?php

namespace Tests\Feature;

use App\Models\User;
use Tests\TestCase;

/**
 * Renders every portal screen against a restored copy of a real tenant
 * database — real people, real invoices, real sessions — rather than fixtures.
 *
 * The default suite runs on SQLite, where the schema does not exist and the
 * CHECK constraints cannot, so this skips unless it is pointed at a restored
 * Postgres tenant:
 *
 *   DB_CONNECTION=pgsql DB_DATABASE=oc_sport_test ./vendor/bin/phpunit --filter PortalSmokeTest
 *
 * Skipping is the honest behaviour: a suite that silently runs on a different
 * database engine than production is blind by construction, and pretending
 * otherwise is worse than saying so.
 */
class PortalSmokeTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();

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


    public function test_every_portal_screen_renders_for_a_real_member(): void
    {
        $user = $this->aMemberAccount();

        $routes = [
            'portal.home' => 'الرئيسية',
            'portal.training' => null,
            'portal.payments' => 'إجمالي المستحق',
            'portal.academy' => null,
            'portal.account' => null,
            'portal.notifications' => null,
            'portal.pass' => null,
            'portal.dues' => null,
            'portal.documents' => null,
            'portal.privacy' => null,
            'portal.requests.create' => null,
            'portal.preferences' => null,
        ];

        foreach ($routes as $name => $needle) {
            $response = $this->actingAs($user)->get(route($name));

            $status = $response->getStatusCode();
            $body = $response->getContent();
            $len = strlen($body);

            fwrite(STDERR, sprintf("  %-22s %d  %6d bytes%s\n", $name, $status, $len,
                $needle && ! str_contains($body, $needle) ? "   MISSING: {$needle}" : ''));

            if ($status !== 200) {
                fwrite(STDERR, "     " . substr(strip_tags($body), 0, 400) . "\n");
            }

            $this->assertSame(200, $status, "{$name} did not render");
        }
    }

    public function test_the_manifest_is_served_per_tenant_and_uncacheable_by_a_proxy(): void
    {
        $response = $this->get(route('portal.manifest'));

        $response->assertOk();
        $response->assertHeader('Cache-Control', 'no-store, private');

        $manifest = $response->json();
        fwrite(STDERR, "  manifest: " . json_encode($manifest, JSON_UNESCAPED_UNICODE) . "\n");

        $this->assertSame('/app/', $manifest['scope']);
        $this->assertSame('/app/', $manifest['start_url']);
    }

    public function test_a_member_cannot_open_another_familys_invoice(): void
    {
        $user = $this->aMemberAccount();

        $foreign = \App\Domain\Financial\Models\Invoice::withoutGlobalScopes()
            ->where('billable_type', \App\Domain\Participant\Models\Participant::class)
            ->whereNotIn('billable_id', app(\App\Domain\Identity\Services\GuardianResolver::class)->participantIdsFor($user))
            ->firstOrFail();

        fwrite(STDERR, "  foreign invoice {$foreign->number} (participant {$foreign->billable_id})\n");

        $this->actingAs($user)->get(route('portal.invoice', $foreign->uuid))->assertForbidden();
    }

    /** The first account on this tenant that actually speaks for a member. */
    private function aMemberAccount(): User
    {
        $resolver = app(\App\Domain\Identity\Services\GuardianResolver::class);

        foreach (User::withoutGlobalScopes()->whereNotNull('person_id')->limit(200)->get() as $candidate) {
            if ($resolver->participantIdsFor($candidate) !== []) {
                return $candidate;
            }
        }

        $this->markTestSkipped('No account on this tenant speaks for a participant.');
    }
}
