<?php

declare(strict_types=1);

namespace Tests\Feature\Demo;

use App\Console\Commands\ResetDemoCompany;
use App\Models\CapitalContribution;
use App\Models\Client;
use App\Models\Company;
use App\Models\Credit;
use App\Models\Partner;
use App\Models\Plan;
use App\Models\SupervisorCollector;
use App\Models\User;
use App\Support\Demo;
use Database\Seeders\DemoDataSeeder;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use PHPUnit\Framework\Attributes\Test;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * DemoDataSeeder puebla la empresa demo compartida con un dataset realista
 * (clientes + créditos con sus cuotas). Debe quedar SIEMPRE scoped a la
 * empresa `is_demo=true`: nunca debe crear ni tocar una fila de otra
 * compañía, sea cual sea el estado previo de la base de datos compartida.
 */
class DemoSeederTest extends TestCase
{
    use DatabaseTransactions;

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

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

        Plan::where('slug', 'professional')->exists() || Plan::factory()->create([
            'slug' => 'professional',
            'is_active' => true,
            'max_clients' => 500,
        ]);

        // --no-seed: aquí probamos el seeder de forma explícita, así que la
        // provisión NO debe sembrar (si no, el segundo seed chocaría con los
        // `identification` únicos DEMO-*).
        $this->artisan('credify:setup-demo --no-seed')->assertSuccessful();

        // La BD compartida de dev/prod puede tener la demo YA poblada; la vaciamos
        // (dentro de la transacción del test → se revierte) para sembrar limpio.
        $demo = Company::where('is_demo', true)->first();
        if ($demo) {
            ResetDemoCompany::wipeTenantData($demo->id);
        }
    }

    #[Test]
    public function it_seeds_a_realistic_dataset_scoped_to_the_demo_company(): void
    {
        $demo = Company::where('is_demo', true)->first();
        $this->assertNotNull($demo);

        // Snapshot ANTES de sembrar: la base de datos compartida de dev ya
        // tiene compañías/clientes reales fuera de esta prueba, así que la
        // invariante de seguridad es "no cambia", no "es cero".
        $clientsOutsideBefore = Client::withoutGlobalScopes()->where('company_id', '!=', $demo->id)->count();
        $creditsOutsideBefore = Credit::withoutGlobalScopes()->where('company_id', '!=', $demo->id)->count();

        $this->seed(DemoDataSeeder::class);

        $demoClientsCount = Client::withoutGlobalScopes()->where('company_id', $demo->id)->count();
        $demoCreditsCount = Credit::withoutGlobalScopes()->where('company_id', $demo->id)->count();

        $this->assertGreaterThan(10, $demoClientsCount);
        $this->assertGreaterThan(10, $demoCreditsCount);

        // El supervisor demo queda enlazado al cobrador (para que su dashboard
        // muestre equipo y datos, no "sin cobradores").
        $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('company_id', $demo->id)
            ->where('supervisor_id', $supervisor->id)
            ->where('collector_id', $collector->id)
            ->count());

        // Socios + capital inicial: sin el aporte, la caja quedaría negativa (los
        // desembolsos sin capital que los respalde). Debe haber socios y capital > 0.
        $this->assertGreaterThan(0, Partner::withoutGlobalScopes()->where('company_id', $demo->id)->count());
        $this->assertGreaterThan(
            0,
            (float) CapitalContribution::withoutGlobalScopes()->where('company_id', $demo->id)->sum('amount')
        );

        // Nada fuera de la demo debe haber cambiado.
        $this->assertSame(
            $clientsOutsideBefore,
            Client::withoutGlobalScopes()->where('company_id', '!=', $demo->id)->count(),
        );
        $this->assertSame(
            $creditsOutsideBefore,
            Credit::withoutGlobalScopes()->where('company_id', '!=', $demo->id)->count(),
        );
    }

    #[Test]
    public function it_warns_and_does_nothing_when_there_is_no_demo_company(): void
    {
        Company::where('is_demo', true)->update(['is_demo' => false]);

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

        $this->seed(DemoDataSeeder::class);

        $this->assertSame(0, Client::withoutGlobalScopes()->where('identification', 'like', 'DEMO-%')->count());
    }
}
