<?php

declare(strict_types=1);

namespace Tests\Feature\Demo;

use App\Console\Commands\ResetDemoCompany;
use App\Models\Client;
use App\Models\Company;
use App\Models\Credit;
use App\Models\DemoCredential;
use App\Models\Plan;
use App\Models\SupervisorCollector;
use App\Models\User;
use App\Support\Demo;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Hash;
use PHPUnit\Framework\Attributes\Test;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * `credify:setup-demo` provisions the single shared demo company (flag
 * `is_demo`) on the Professional plan with its 3 fixed per-role users.
 * Must be idempotent: running it again must not create a second demo
 * company or duplicate the demo users.
 */
class SetupDemoTest extends TestCase
{
    use DatabaseTransactions;

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

        foreach (['admin', 'supervisor', 'collector'] as $role) {
            Role::firstOrCreate(['name' => $role, 'guard_name' => 'web']);
        }

        // El plan 'professional' ya existe en la DB de dev (única por slug);
        // solo se crea si falta (p.ej. DB fresca de CI).
        Plan::where('slug', 'professional')->exists() || Plan::factory()->create([
            'slug' => 'professional',
            'is_active' => true,
            'max_clients' => 500,
        ]);

        // La BD compartida de dev/prod puede tener la demo YA poblada; la vaciamos
        // (dentro de la transacción del test → se revierte) para que las
        // aserciones de "siembra si está vacía" partan de un estado limpio.
        $demo = Company::where('is_demo', true)->first();
        if ($demo) {
            ResetDemoCompany::wipeTenantData($demo->id);
        }
    }

    #[Test]
    public function it_provisions_the_demo_company_on_the_professional_plan_with_its_three_users(): void
    {
        $this->artisan('credify:setup-demo')->assertSuccessful();

        $this->assertSame(1, Company::where('is_demo', true)->count());

        $company = Company::where('is_demo', true)->first();
        $this->assertNotNull($company);
        $this->assertNotNull($company->currentSubscription);
        $this->assertSame('professional', $company->currentSubscription->plan->slug);

        foreach (Demo::USERS as $email => $role) {
            $user = User::where('email', $email)->first();
            $this->assertNotNull($user, "el usuario demo {$email} debe existir");
            $this->assertSame($company->id, $user->company_id);
            $this->assertTrue($user->hasRole($role));
        }
    }

    #[Test]
    public function it_seeds_sample_data_so_the_demo_is_not_empty(): void
    {
        $this->artisan('credify:setup-demo')->assertSuccessful();

        $company = Company::where('is_demo', true)->first();
        $this->assertNotNull($company);

        // Un visitante debe ver la app con datos, no en ceros.
        $this->assertGreaterThan(0, Client::withoutGlobalScopes()->where('company_id', $company->id)->count());
        $this->assertGreaterThan(0, Credit::withoutGlobalScopes()->where('company_id', $company->id)->count());

        // El supervisor demo debe tener al cobrador en su equipo (si no, su
        // dashboard saldría vacío).
        $supervisor = User::where('email', Demo::ROLE_SLUG_TO_EMAIL['supervisor'])->first();
        $collector = User::where('email', Demo::ROLE_SLUG_TO_EMAIL['cobrador'])->first();
        $this->assertSame(1, SupervisorCollector::withoutGlobalScopes()
            ->where('supervisor_id', $supervisor->id)
            ->where('collector_id', $collector->id)
            ->count());
    }

    #[Test]
    public function it_is_idempotent_and_does_not_duplicate_the_seeded_data(): void
    {
        $this->artisan('credify:setup-demo')->assertSuccessful();

        $company = Company::where('is_demo', true)->first();
        $clientsAfterFirst = Client::withoutGlobalScopes()->where('company_id', $company->id)->count();

        $this->artisan('credify:setup-demo')->assertSuccessful();

        $this->assertSame(1, Company::where('is_demo', true)->count());
        // La segunda corrida no re-siembra (la demo ya tenía datos).
        $this->assertSame(
            $clientsAfterFirst,
            Client::withoutGlobalScopes()->where('company_id', $company->id)->count()
        );
    }

    #[Test]
    public function it_sets_known_demo_passwords_that_authenticate(): void
    {
        $this->artisan('credify:setup-demo --no-seed')->assertSuccessful();

        $cred = DemoCredential::where('role', 'admin')->first();
        $this->assertNotNull($cred);
        $user = User::where('email', Demo::ROLE_SLUG_TO_EMAIL['dueno'])->first();
        $this->assertTrue(Hash::check($cred->password, $user->password));
    }
}
